diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,40 @@
+0.13.3.0
+===
+
+- Added `Read` instance for `Polar` in `NumHask.Algebra.Metric`.
+- Added `NumHask.Algebra.Tropical` with the `MinPlus` tropical semiring.
+- Added `StarSemiring` and `KleeneAlgebra` instances for `Bool`.
+- Added `Magma`, `Unital` and `Idempotent` instances for `Bool`.
+- Documented `StarSemiring` and `KleeneAlgebra` laws with the Conway equations.
+
+0.13.2.1
+===
+
+- removed doctest-parallel test suite, use cabal-docspec in CI instead
+- updated CI to use cabal-docspec 0.0.0.20250606
+
+0.13.2
+===
+
+- GHC 9.14.1 support
+- simplified Cabal stanzas to best practice template
+- fixed asinh bug in Field module
+
+0.13.1
+===
+
+- added Data instances
+
+
+0.13
+===
+
+- added modF, divF and divModF as field versions of modulo and diviso Integral functions.
+- fixed bug in EuclideanPair log function.
+- BoundedJoinSemiLattice becomes LowerBounded
+- BoundedMeetSemiLattice becomes UpperBounded
+- switch to GHC2024
+
 0.12.1
 ===
 
diff --git a/numhask.cabal b/numhask.cabal
--- a/numhask.cabal
+++ b/numhask.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: numhask
-version: 0.12.1.0
+version: 0.13.3.0
 license: BSD-3-Clause
 license-file: LICENSE
 copyright: Tony Day (c) 2016
@@ -11,79 +11,67 @@
 bug-reports: https://github.com/tonyday567/numhask/issues
 synopsis: A numeric class hierarchy.
 description:
-    This package provides alternative numeric classes over Prelude.
+  This package provides alternative numeric classes over Prelude.
 
-    The numeric class constellation looks somewhat like:
+  The numeric class constellation looks somewhat like:
 
-    ![nh](docs/other/nh12.svg)
+  ![nh](docs/other/nh12.svg)
 
-    == Usage
+  == Usage
 
-    >>> {-# LANGUAGE GHC2021 #-}
-    >>> {-# LANGUAGE RebindableSyntax #-}
-    >>> import NumHask.Prelude
+  >>> {-# LANGUAGE GHC2024 #-}
+  >>> {-# LANGUAGE RebindableSyntax #-}
+  >>> import NumHask.Prelude
 
-    See "NumHask" for a detailed overview.
+  See "NumHask" for a detailed overview.
 
 build-type: Simple
 tested-with:
-    , GHC == 9.10.1
-    , GHC == 9.6.5
-    , GHC == 9.8.2
+  ghc ==9.10.3
+  ghc ==9.12.2
+  ghc ==9.14.1
+
 extra-doc-files:
-    ChangeLog.md
-    other/*.svg
-    readme.md
+  ChangeLog.md
+  other/*.svg
+  readme.md
 
 source-repository head
-    type: git
-    location: https://github.com/tonyday567/numhask
+  type: git
+  location: https://github.com/tonyday567/numhask
 
 common ghc-options-stanza
-    ghc-options:
-        -Wall
-        -Wcompat
-        -Widentities
-        -Wincomplete-record-updates
-        -Wincomplete-uni-patterns
-        -Wpartial-fields
-        -Wredundant-constraints
-
-common ghc2021-stanza
-    default-language: GHC2021
+  ghc-options:
+    -Wall
+    -Wcompat
+    -Wincomplete-record-updates
+    -Wincomplete-uni-patterns
+    -Wredundant-constraints
 
 library
-    import: ghc-options-stanza
-    import: ghc2021-stanza
-    hs-source-dirs: src
-    build-depends: , base >=4.14 && <5
-    exposed-modules:
-        NumHask
-        NumHask.Algebra.Action
-        NumHask.Algebra.Additive
-        NumHask.Algebra.Field
-        NumHask.Algebra.Group
-        NumHask.Algebra.Lattice
-        NumHask.Algebra.Metric
-        NumHask.Algebra.Multiplicative
-        NumHask.Algebra.Ring
-        NumHask.Data.Complex
-        NumHask.Data.Integral
-        NumHask.Data.Positive
-        NumHask.Data.Rational
-        NumHask.Data.Wrapped
-        NumHask.Exception
-        NumHask.Prelude
-    default-extensions: RebindableSyntax
+  import: ghc-options-stanza
+  default-language: GHC2024
+  hs-source-dirs: src
+  build-depends: base >=4.14 && <5
+  exposed-modules:
+    NumHask
+    NumHask.Algebra.Action
+    NumHask.Algebra.Additive
+    NumHask.Algebra.Field
+    NumHask.Algebra.Group
+    NumHask.Algebra.Lattice
+    NumHask.Algebra.Metric
+    NumHask.Algebra.Multiplicative
+    NumHask.Algebra.Patterns
+    NumHask.Algebra.Ring
+    NumHask.Algebra.Tropical
+    NumHask.Data.Complex
+    NumHask.Data.Integral
+    NumHask.Data.Positive
+    NumHask.Data.Rational
+    NumHask.Data.Wrapped
+    NumHask.Exception
+    NumHask.Prelude
 
-test-suite doctests
-    import: ghc2021-stanza
-    main-is: doctests.hs
-    hs-source-dirs: test
-    build-depends:
-        , QuickCheck       >=2.14 && <2.16
-        , base             >=4.14 && <5
-        , doctest-parallel >=0.3 && <0.4
-    default-extensions: RebindableSyntax
-    ghc-options: -threaded
-    type: exitcode-stdio-1.0
+  default-extensions: RebindableSyntax
+
diff --git a/readme.md b/readme.md
--- a/readme.md
+++ b/readme.md
@@ -2,7 +2,7 @@
 ===
 
 [![Hackage](https://img.shields.io/hackage/v/numhask.svg)](https://hackage.haskell.org/package/numhask)
-[![Build Status](https://github.com/tonyday567/numhask/workflows/haskell-ci/badge.svg)](https://github.com/tonyday567/numhask/actions?query=workflow%3Ahaskell-ci)
+[![build](https://github.com/tonyday567/numhask/actions/workflows/haskell-ci.yml/badge.svg)](https://github.com/tonyday567/numhask/actions/workflows/haskell-ci.yml)
 
 ![](other/nh12.svg)
 
@@ -145,7 +145,7 @@
       DivisiveAction,
       JoinSemiLattice,
       MeetSemiLattice,
-      BoundedMeetSemiLattice
+      UpperBounded
     )
     via (Wrapped a)
 ```
diff --git a/src/NumHask.hs b/src/NumHask.hs
--- a/src/NumHask.hs
+++ b/src/NumHask.hs
@@ -36,6 +36,9 @@
     InvolutiveRing (..),
     two,
 
+    -- * Tropical
+    MinPlus (..),
+
     -- * Field
     Field,
     ExpField (..),
@@ -45,6 +48,9 @@
     negInfinity,
     nan,
     half,
+    modF,
+    divF,
+    divModF,
 
     -- * Lattice
     JoinSemiLattice (..),
@@ -53,8 +59,8 @@
     MeetSemiLattice (..),
     meetLeq,
     (</),
-    BoundedJoinSemiLattice (..),
-    BoundedMeetSemiLattice (..),
+    LowerBounded (..),
+    UpperBounded (..),
 
     -- * Action
     AdditiveAction (..),
@@ -66,6 +72,7 @@
     DivisiveAction (..),
     (/|),
     Module,
+    TrivialAction (..),
 
     -- * Metric
     Basis (..),
@@ -89,6 +96,7 @@
     (+:),
     realPart,
     imagPart,
+    normSquared,
 
     -- * Integral
     Integral (..),
@@ -101,6 +109,7 @@
     odd,
     (^^),
     (^),
+    (^+),
 
     -- * Rational
     Ratio (..),
@@ -110,6 +119,8 @@
     FromRational (..),
     reduce,
     gcd,
+    numerator,
+    denominator,
 
     -- * Exceptions
     NumHaskException (..),
@@ -123,6 +134,7 @@
     Module,
     MultiplicativeAction (..),
     SubtractiveAction (..),
+    TrivialAction (..),
     (*|),
     (+|),
     (-|),
@@ -139,16 +151,19 @@
     Field,
     QuotientField (..),
     TrigField (..),
+    divF,
+    divModF,
     half,
     infinity,
+    modF,
     nan,
     negInfinity,
   )
 import NumHask.Algebra.Lattice
-  ( BoundedJoinSemiLattice (..),
-    BoundedMeetSemiLattice (..),
-    JoinSemiLattice (..),
+  ( JoinSemiLattice (..),
+    LowerBounded (..),
     MeetSemiLattice (..),
+    UpperBounded (..),
     joinLeq,
     meetLeq,
     (</),
@@ -185,7 +200,14 @@
     StarSemiring (..),
     two,
   )
-import NumHask.Data.Complex (Complex (..), imagPart, realPart, (+:))
+import NumHask.Algebra.Tropical (MinPlus (..))
+import NumHask.Data.Complex
+  ( Complex (..),
+    imagPart,
+    normSquared,
+    realPart,
+    (+:),
+  )
 import NumHask.Data.Integral
   ( FromInt,
     FromInteger (..),
@@ -196,6 +218,7 @@
     even,
     odd,
     (^),
+    (^+),
     (^^),
   )
 import NumHask.Data.Rational
@@ -204,7 +227,9 @@
     Ratio (..),
     Rational,
     ToRatio (..),
+    denominator,
     gcd,
+    numerator,
     reduce,
   )
 import NumHask.Exception (NumHaskException (..), throw)
diff --git a/src/NumHask/Algebra/Action.hs b/src/NumHask/Algebra/Action.hs
--- a/src/NumHask/Algebra/Action.hs
+++ b/src/NumHask/Algebra/Action.hs
@@ -13,14 +13,15 @@
     DivisiveAction (..),
     (/|),
     Module,
+    TrivialAction (..),
   )
 where
 
 import Data.Kind (Type)
-import NumHask.Algebra.Additive (Additive, Subtractive, negate)
-import NumHask.Algebra.Multiplicative (Divisive, Multiplicative, recip)
+import NumHask.Algebra.Additive (Additive (..), Subtractive (..))
+import NumHask.Algebra.Multiplicative (Divisive (..), Multiplicative (..))
 import NumHask.Algebra.Ring (Distributive)
-import Prelude (flip)
+import Prelude (Eq, Ord, flip)
 
 -- | Additive Action
 --
@@ -110,3 +111,23 @@
 -- > a *| zero == zero
 -- > a *| b == b |* a
 type Module m = (Distributive (Scalar m), MultiplicativeAction m)
+
+-- | An action of a set of numbers on itself
+newtype TrivialAction a = TrivialAction
+  { getTrivialAction :: a
+  }
+  deriving (Eq, Ord, Additive, Subtractive, Multiplicative, Divisive)
+
+instance (Additive a) => AdditiveAction (TrivialAction a) where
+  type AdditiveScalar (TrivialAction a) = a
+  TrivialAction a |+ b = TrivialAction (a + b)
+
+instance (Subtractive a) => SubtractiveAction (TrivialAction a) where
+  TrivialAction a |- b = TrivialAction (a - b)
+
+instance (Multiplicative a) => MultiplicativeAction (TrivialAction a) where
+  type Scalar (TrivialAction a) = a
+  TrivialAction a |* b = TrivialAction (a * b)
+
+instance (Divisive a) => DivisiveAction (TrivialAction a) where
+  TrivialAction a |/ b = TrivialAction (a / b)
diff --git a/src/NumHask/Algebra/Additive.hs b/src/NumHask/Algebra/Additive.hs
--- a/src/NumHask/Algebra/Additive.hs
+++ b/src/NumHask/Algebra/Additive.hs
@@ -102,6 +102,7 @@
   zero = 0
 
 instance Subtractive Double where
+  (-) = (P.-)
   negate = P.negate
 
 instance Additive Float where
@@ -109,6 +110,7 @@
   zero = 0
 
 instance Subtractive Float where
+  (-) = (P.-)
   negate = P.negate
 
 instance Additive Int where
@@ -116,6 +118,7 @@
   zero = 0
 
 instance Subtractive Int where
+  (-) = (P.-)
   negate = P.negate
 
 instance Additive Integer where
@@ -123,6 +126,7 @@
   zero = 0
 
 instance Subtractive Integer where
+  (-) = (P.-)
   negate = P.negate
 
 instance Additive Bool where
@@ -134,6 +138,7 @@
   zero = 0
 
 instance Subtractive Natural where
+  (-) = (P.-)
   negate = P.negate
 
 instance Additive Int8 where
@@ -141,6 +146,7 @@
   zero = 0
 
 instance Subtractive Int8 where
+  (-) = (P.-)
   negate = P.negate
 
 instance Additive Int16 where
@@ -148,6 +154,7 @@
   zero = 0
 
 instance Subtractive Int16 where
+  (-) = (P.-)
   negate = P.negate
 
 instance Additive Int32 where
@@ -155,6 +162,7 @@
   zero = 0
 
 instance Subtractive Int32 where
+  (-) = (P.-)
   negate = P.negate
 
 instance Additive Int64 where
@@ -162,6 +170,7 @@
   zero = 0
 
 instance Subtractive Int64 where
+  (-) = (P.-)
   negate = P.negate
 
 instance Additive Word where
@@ -169,6 +178,7 @@
   zero = 0
 
 instance Subtractive Word where
+  (-) = (P.-)
   negate = P.negate
 
 instance Additive Word8 where
@@ -176,6 +186,7 @@
   zero = 0
 
 instance Subtractive Word8 where
+  (-) = (P.-)
   negate = P.negate
 
 instance Additive Word16 where
@@ -183,6 +194,7 @@
   zero = 0
 
 instance Subtractive Word16 where
+  (-) = (P.-)
   negate = P.negate
 
 instance Additive Word32 where
@@ -190,6 +202,7 @@
   zero = 0
 
 instance Subtractive Word32 where
+  (-) = (P.-)
   negate = P.negate
 
 instance Additive Word64 where
@@ -197,6 +210,7 @@
   zero = 0
 
 instance Subtractive Word64 where
+  (-) = (P.-)
   negate = P.negate
 
 instance (Additive b) => Additive (a -> b) where
@@ -204,4 +218,5 @@
   zero _ = zero
 
 instance (Subtractive b) => Subtractive (a -> b) where
+  f - f' = \a -> f a - f' a
   negate f = negate P.. f
diff --git a/src/NumHask/Algebra/Field.hs b/src/NumHask/Algebra/Field.hs
--- a/src/NumHask/Algebra/Field.hs
+++ b/src/NumHask/Algebra/Field.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE DefaultSignatures #-}
 {-# LANGUAGE TypeFamilies #-}
 
--- | Field classes
+-- | [field](https://en.wikipedia.org/wiki/Field_(mathematics\)) classes
 module NumHask.Algebra.Field
   ( SemiField,
     Field,
@@ -12,6 +12,9 @@
     nan,
     TrigField (..),
     half,
+    modF,
+    divF,
+    divModF,
   )
 where
 
@@ -24,8 +27,8 @@
     (/),
   )
 import NumHask.Algebra.Ring (Distributive, Ring, two)
-import NumHask.Data.Integral (Integral, even)
-import Prelude ((.))
+import NumHask.Data.Integral (FromIntegral (..), Integral, even)
+import Prelude (Eq (..), (.))
 import Prelude qualified as P
 
 -- $setup
@@ -35,7 +38,7 @@
 -- >>> :set -XScopedTypeVariables
 -- >>> import NumHask.Prelude
 
--- | A <https://en.wikipedia.org/wiki/Semifield Semifield> is a field with no substraction.
+-- | A <https://en.wikipedia.org/wiki/Semifield Semifield> is a field with no subtraction.
 --
 -- @since 0.12
 type SemiField a = (Distributive a, Divisive a)
@@ -239,7 +242,7 @@
   atan2 = P.atan2
   sinh = P.sinh
   cosh = P.cosh
-  asinh = P.sinh
+  asinh = P.asinh
   acosh = P.acosh
   atanh = P.atanh
 
@@ -253,7 +256,7 @@
   atan2 = P.atan2
   sinh = P.sinh
   cosh = P.cosh
-  asinh = P.sinh
+  asinh = P.asinh
   acosh = P.acosh
   atanh = P.atanh
 
@@ -277,3 +280,43 @@
 -- 0.5
 half :: (Additive a, Divisive a) => a
 half = one / two
+
+-- | Approximate modulo for fields
+--
+-- @since 0.13
+--
+-- >>> modF 1.5 1.2
+-- 0.30000000000000004
+modF :: (Eq a, Field a, FromIntegral a (Whole a), QuotientField a) => a -> a -> a
+modF n d
+  | d == infinity = n
+  | d == zero = nan
+  | P.True = n - d * fromIntegral (floor (n / d))
+
+-- | Approximate diviso for fields.
+--
+-- Compared with 'NumHask.Algebra.Field.div', divF returns the original type rather than the 'Whole' type.
+--
+-- @since 0.13
+--
+-- >>> divF 1.5 1.2
+-- 1.0
+divF :: (Eq a, Field a, FromIntegral a (Whole a), QuotientField a) => a -> a -> a
+divF n d
+  | d == infinity = zero
+  | d == zero = infinity
+  | P.True = fromIntegral (floor (n / d))
+
+-- | Approximate `NumHask.Algebra.Field.divMod` for fields.
+--
+-- @since 0.13
+--
+-- >>> divModF 1.5 1.2
+-- (1.0,0.30000000000000004)
+divModF :: (Eq a, Field a, FromIntegral a (Whole a), QuotientField a) => a -> a -> (a, a)
+divModF n d
+  | d == infinity = (zero, n)
+  | d == zero = (infinity, nan)
+  | P.True = (div', n - d * div')
+  where
+    div' = fromIntegral (floor (n / d))
diff --git a/src/NumHask/Algebra/Group.hs b/src/NumHask/Algebra/Group.hs
--- a/src/NumHask/Algebra/Group.hs
+++ b/src/NumHask/Algebra/Group.hs
@@ -40,6 +40,12 @@
 instance (Magma b) => Magma (a -> b) where
   f ⊕ g = \a -> f a ⊕ g a
 
+instance Magma Bool where
+  a ⊕ b = a || b
+
+instance Unital Bool where
+  unit = False
+
 -- | A Unital Magma is a magma with an
 --   <https://en.wikipedia.org/wiki/Identity_element identity element> (the
 --   unit).
@@ -115,6 +121,8 @@
   Idempotent a
 
 instance (Idempotent b) => Idempotent (a -> b)
+
+instance Idempotent Bool
 
 -- | An <https://en.wikipedia.org/wiki/Abelian_group Abelian Group> is an
 --   Associative, Unital, Invertible and Commutative Magma . In other words, it
diff --git a/src/NumHask/Algebra/Lattice.hs b/src/NumHask/Algebra/Lattice.hs
--- a/src/NumHask/Algebra/Lattice.hs
+++ b/src/NumHask/Algebra/Lattice.hs
@@ -6,8 +6,8 @@
     MeetSemiLattice (..),
     meetLeq,
     (</),
-    BoundedJoinSemiLattice (..),
-    BoundedMeetSemiLattice (..),
+    LowerBounded (..),
+    UpperBounded (..),
     Lattice,
     BoundedLattice,
   )
@@ -76,45 +76,45 @@
 
 -- | A join-semilattice with an identity element 'bottom' for '\/'.
 --
--- > x \/ bottom == bottom
-class (JoinSemiLattice a) => BoundedJoinSemiLattice a where
+-- > x \/ bottom == x
+class (JoinSemiLattice a) => LowerBounded a where
   bottom :: a
 
 -- | A meet-semilattice with an identity element 'top' for '/\'.
 --
--- > x /\ top == top
-class (MeetSemiLattice a) => BoundedMeetSemiLattice a where
+-- > x /\ top == x
+class (MeetSemiLattice a) => UpperBounded a where
   top :: a
 
 -- | Lattices with both bounds
 --
--- > x /\ bottom == x
--- > x \/ top = x
-type BoundedLattice a = (JoinSemiLattice a, MeetSemiLattice a, BoundedJoinSemiLattice a, BoundedMeetSemiLattice a)
+-- > x /\ bottom == bottom
+-- > x \/ top == top
+type BoundedLattice a = (JoinSemiLattice a, MeetSemiLattice a, LowerBounded a, UpperBounded a)
 
 instance JoinSemiLattice Float where
-  (\/) = min
+  (\/) = max
 
 instance MeetSemiLattice Float where
-  (/\) = max
+  (/\) = min
 
 instance JoinSemiLattice Double where
-  (\/) = min
+  (\/) = max
 
 instance MeetSemiLattice Double where
-  (/\) = max
+  (/\) = min
 
 instance JoinSemiLattice Int where
-  (\/) = min
+  (\/) = max
 
 instance MeetSemiLattice Int where
-  (/\) = max
+  (/\) = min
 
 instance JoinSemiLattice Integer where
-  (\/) = min
+  (\/) = max
 
 instance MeetSemiLattice Integer where
-  (/\) = max
+  (/\) = min
 
 instance JoinSemiLattice Bool where
   (\/) = (||)
@@ -123,142 +123,142 @@
   (/\) = (&&)
 
 instance JoinSemiLattice Natural where
-  (\/) = min
+  (\/) = max
 
 instance MeetSemiLattice Natural where
-  (/\) = max
+  (/\) = min
 
 instance JoinSemiLattice Int8 where
-  (\/) = min
+  (\/) = max
 
 instance MeetSemiLattice Int8 where
-  (/\) = max
+  (/\) = min
 
 instance JoinSemiLattice Int16 where
-  (\/) = min
+  (\/) = max
 
 instance MeetSemiLattice Int16 where
-  (/\) = max
+  (/\) = min
 
 instance JoinSemiLattice Int32 where
-  (\/) = min
+  (\/) = max
 
 instance MeetSemiLattice Int32 where
-  (/\) = max
+  (/\) = min
 
 instance JoinSemiLattice Int64 where
-  (\/) = min
+  (\/) = max
 
 instance MeetSemiLattice Int64 where
-  (/\) = max
+  (/\) = min
 
 instance JoinSemiLattice Word where
-  (\/) = min
+  (\/) = max
 
 instance MeetSemiLattice Word where
-  (/\) = max
+  (/\) = min
 
 instance JoinSemiLattice Word8 where
-  (\/) = min
+  (\/) = max
 
 instance MeetSemiLattice Word8 where
-  (/\) = max
+  (/\) = min
 
 instance JoinSemiLattice Word16 where
-  (\/) = min
+  (\/) = max
 
 instance MeetSemiLattice Word16 where
-  (/\) = max
+  (/\) = min
 
 instance JoinSemiLattice Word32 where
-  (\/) = min
+  (\/) = max
 
 instance MeetSemiLattice Word32 where
-  (/\) = max
+  (/\) = min
 
 instance JoinSemiLattice Word64 where
-  (\/) = min
+  (\/) = max
 
 instance MeetSemiLattice Word64 where
-  (/\) = max
+  (/\) = min
 
-instance BoundedJoinSemiLattice Float where
+instance LowerBounded Float where
   bottom = negInfinity
 
-instance BoundedMeetSemiLattice Float where
+instance UpperBounded Float where
   top = infinity
 
-instance BoundedJoinSemiLattice Double where
+instance LowerBounded Double where
   bottom = negInfinity
 
-instance BoundedMeetSemiLattice Double where
+instance UpperBounded Double where
   top = infinity
 
-instance BoundedJoinSemiLattice Int where
+instance LowerBounded Int where
   bottom = minBound
 
-instance BoundedMeetSemiLattice Int where
+instance UpperBounded Int where
   top = maxBound
 
-instance BoundedJoinSemiLattice Bool where
+instance LowerBounded Bool where
   bottom = False
 
-instance BoundedMeetSemiLattice Bool where
+instance UpperBounded Bool where
   top = True
 
-instance BoundedJoinSemiLattice Natural where
+instance LowerBounded Natural where
   bottom = zero
 
-instance BoundedJoinSemiLattice Int8 where
+instance LowerBounded Int8 where
   bottom = minBound
 
-instance BoundedMeetSemiLattice Int8 where
+instance UpperBounded Int8 where
   top = maxBound
 
-instance BoundedJoinSemiLattice Int16 where
+instance LowerBounded Int16 where
   bottom = minBound
 
-instance BoundedMeetSemiLattice Int16 where
+instance UpperBounded Int16 where
   top = maxBound
 
-instance BoundedJoinSemiLattice Int32 where
+instance LowerBounded Int32 where
   bottom = minBound
 
-instance BoundedMeetSemiLattice Int32 where
+instance UpperBounded Int32 where
   top = maxBound
 
-instance BoundedJoinSemiLattice Int64 where
+instance LowerBounded Int64 where
   bottom = minBound
 
-instance BoundedMeetSemiLattice Int64 where
+instance UpperBounded Int64 where
   top = maxBound
 
-instance BoundedJoinSemiLattice Word where
+instance LowerBounded Word where
   bottom = minBound
 
-instance BoundedMeetSemiLattice Word where
+instance UpperBounded Word where
   top = maxBound
 
-instance BoundedJoinSemiLattice Word8 where
+instance LowerBounded Word8 where
   bottom = minBound
 
-instance BoundedMeetSemiLattice Word8 where
+instance UpperBounded Word8 where
   top = maxBound
 
-instance BoundedJoinSemiLattice Word16 where
+instance LowerBounded Word16 where
   bottom = minBound
 
-instance BoundedMeetSemiLattice Word16 where
+instance UpperBounded Word16 where
   top = maxBound
 
-instance BoundedJoinSemiLattice Word32 where
+instance LowerBounded Word32 where
   bottom = minBound
 
-instance BoundedMeetSemiLattice Word32 where
+instance UpperBounded Word32 where
   top = maxBound
 
-instance BoundedJoinSemiLattice Word64 where
+instance LowerBounded Word64 where
   bottom = minBound
 
-instance BoundedMeetSemiLattice Word64 where
+instance UpperBounded Word64 where
   top = maxBound
diff --git a/src/NumHask/Algebra/Metric.hs b/src/NumHask/Algebra/Metric.hs
--- a/src/NumHask/Algebra/Metric.hs
+++ b/src/NumHask/Algebra/Metric.hs
@@ -1,9 +1,6 @@
-{-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE DerivingVia #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE UndecidableInstances #-}
-{-# OPTIONS_GHC -Wno-name-shadowing #-}
-{-# OPTIONS_GHC -Wno-type-equality-out-of-scope #-}
 
 -- | Metric classes
 module NumHask.Algebra.Metric
@@ -28,8 +25,10 @@
 
 import Control.Applicative
 import Data.Bool
+import Data.Data
 import Data.Int (Int16, Int32, Int64, Int8)
 import Data.Kind
+import Data.Type.Equality
 import Data.Word (Word16, Word32, Word64, Word8)
 import GHC.Generics
 import GHC.Natural (Natural (..))
@@ -39,7 +38,7 @@
 import NumHask.Algebra.Lattice
 import NumHask.Algebra.Multiplicative
 import NumHask.Algebra.Ring
-import Prelude (Double, Eq (..), Float, Functor (..), Int, Integer, Ord, Show, Word, fromRational)
+import Prelude (Double, Eq (..), Float, Functor (..), Int, Integer, Read, Show, Word, fromRational)
 import Prelude qualified as P
 
 -- $setup
@@ -222,7 +221,7 @@
 --
 -- See [Polar coordinate system](https://en.wikipedia.org/wiki/Polar_coordinate_system)
 data Polar a = Polar {radial :: a, azimuth :: a}
-  deriving (Generic, Show, Eq)
+  deriving (Eq, Show, Read, Generic, Data)
 
 instance (Additive a, Multiplicative a) => Basis (Polar a) where
   type Mag (Polar a) = a
@@ -258,7 +257,7 @@
 -- >>> nearZero (epsilon :: EuclideanPair Double)
 -- True
 nearZero :: (Epsilon a, Lattice a, Subtractive a) => a -> Bool
-nearZero a = epsilon /\ a == epsilon && epsilon /\ negate a == epsilon
+nearZero a = a /\ epsilon == a && negate a /\ epsilon == negate a
 
 -- | Approximate equality
 --
@@ -312,10 +311,7 @@
 -- @since 0.11
 newtype EuclideanPair a = EuclideanPair {euclidPair :: (a, a)}
   deriving stock
-    ( Generic,
-      Eq,
-      Show
-    )
+    (Eq, Show, Generic, Data)
 
 instance Functor EuclideanPair where
   fmap f (EuclideanPair (x, y)) = EuclideanPair (f x, f y)
@@ -330,6 +326,7 @@
   zero = pure zero
 
 instance (Subtractive a) => Subtractive (EuclideanPair a) where
+  (-) = liftA2 (-)
   negate = fmap negate
 
 instance
@@ -340,7 +337,7 @@
   one = pure one
 
 instance
-  (Subtractive a, Divisive a) =>
+  (Divisive a) =>
   Divisive (EuclideanPair a)
   where
   recip = fmap recip
@@ -372,10 +369,10 @@
 instance (MeetSemiLattice a) => MeetSemiLattice (EuclideanPair a) where
   (/\) (EuclideanPair (x, y)) (EuclideanPair (x', y')) = EuclideanPair (x /\ x', y /\ y')
 
-instance (BoundedJoinSemiLattice a) => BoundedJoinSemiLattice (EuclideanPair a) where
+instance (LowerBounded a) => LowerBounded (EuclideanPair a) where
   bottom = pure bottom
 
-instance (BoundedMeetSemiLattice a) => BoundedMeetSemiLattice (EuclideanPair a) where
+instance (UpperBounded a) => UpperBounded (EuclideanPair a) where
   top = pure top
 
 instance (Multiplicative a) => MultiplicativeAction (EuclideanPair a) where
@@ -385,21 +382,11 @@
 instance (Divisive a) => DivisiveAction (EuclideanPair a) where
   (|/) e s = fmap (/ s) e
 
-instance (Ord a, TrigField a, ExpField a) => ExpField (EuclideanPair a) where
+instance (TrigField a, ExpField a) => ExpField (EuclideanPair a) where
   exp (EuclideanPair (x, y)) = EuclideanPair (exp x * cos y, exp x * sin y)
-  log (EuclideanPair (x, y)) = EuclideanPair (log (sqrt (x * x + y * y)), atan2' y x)
-    where
-      atan2' y x
-        | x P.> zero = atan (y / x)
-        | x P.== zero P.&& y P.> zero = pi / (one + one)
-        | x P.< one P.&& y P.> one = pi + atan (y / x)
-        | (x P.<= zero P.&& y P.< zero) || (x P.< zero) =
-            negate (atan2' (negate y) x)
-        | y P.== zero = pi -- must be after the previous test on zero y
-        | x P.== zero P.&& y P.== zero = y -- must be after the other double zero tests
-        | P.otherwise = x + y -- x or y is a NaN, return a NaN (via +)
+  log (EuclideanPair (x, y)) = EuclideanPair (log (sqrt (x * x + y * y)), atan2 y x)
 
-instance (QuotientField a, Subtractive a) => QuotientField (EuclideanPair a) where
+instance (QuotientField a) => QuotientField (EuclideanPair a) where
   type Whole (EuclideanPair a) = EuclideanPair (Whole a)
 
   properFraction (EuclideanPair (x, y)) =
diff --git a/src/NumHask/Algebra/Patterns.hs b/src/NumHask/Algebra/Patterns.hs
new file mode 100644
--- /dev/null
+++ b/src/NumHask/Algebra/Patterns.hs
@@ -0,0 +1,43 @@
+{-# LANGUAGE PatternSynonyms #-}
+{-# LANGUAGE ViewPatterns #-}
+
+-- | Patterns for common tests
+module NumHask.Algebra.Patterns
+  ( pattern Zero,
+    pattern One,
+    pattern MinusOne,
+  )
+where
+
+import NumHask.Algebra.Additive
+import NumHask.Algebra.Multiplicative
+import Prelude (Bool (..), Eq (..), (.))
+
+-- | Enabling pattern matching on zero:
+--
+-- > isItZero Zero = True
+-- > isItZero _    = False
+pattern Zero :: forall a. (Eq a, Additive a) => a
+pattern Zero <- ((== zero) -> True)
+
+-- | Enabling pattern matching on one:
+--
+-- > isItOne One = True
+-- > isItOne _   = False
+pattern One :: forall a. (Eq a, Multiplicative a) => a
+pattern One <- ((== one) -> True)
+
+-- | Enabling pattern matching on minus one:
+--
+-- > isItMinusOne MinusOne = True
+-- > isItMinusOne _        = False
+--
+-- The means of testing (that is, add one, and check if it equals
+-- zero) might be surprising. Other, more obvious, methods would
+-- result in underflow errors. (For example, we could negate and test
+-- if it's equal to one, but that would fail on any nonzero
+-- 'Natural'. Similarly, we could test for equality with the negation of
+-- one, but that would fail on any 'Natural' whatsoever, since 'negate
+-- one' underflows.)
+pattern MinusOne :: forall a. (Eq a, Additive a, Multiplicative a) => a
+pattern MinusOne <- ((== zero) . (+ one) -> True)
diff --git a/src/NumHask/Algebra/Ring.hs b/src/NumHask/Algebra/Ring.hs
--- a/src/NumHask/Algebra/Ring.hs
+++ b/src/NumHask/Algebra/Ring.hs
@@ -52,9 +52,13 @@
 -- > \a -> a * zero == zero
 type Ring a = (Distributive a, Subtractive a)
 
--- | A <https://en.wikipedia.org/wiki/Semiring#Star_semirings StarSemiring> is a semiring with an additional unary operator (star) satisfying:
+-- | A <https://en.wikipedia.org/wiki/Semiring#Star_semirings StarSemiring> is a semiring with a unary star operator satisfying the Conway equations:
 --
--- > \a -> star a == one + a * star a
+-- > \a -> star a == one + a * star a                                  -- fixpoint
+-- > \a b -> star (a * b) == one + a * star (b * a) * b                -- product-star (sliding)
+-- > \a b -> star (a + b) == star (star a * b) * star a                -- sum-star (vanishing)
+--
+-- These three equations are the doctestable core; they are exactly the sliding and vanishing axioms of a traced category in semiring clothing.
 class (Distributive a) => StarSemiring a where
   {-# MINIMAL star | plus #-}
 
@@ -66,9 +70,26 @@
 
 -- | A <https://en.wikipedia.org/wiki/Kleene_algebra Kleene Algebra> is a Star Semiring with idempotent addition.
 --
--- > a * x + x = a ==> star a * x + x = x
--- > x * a + x = a ==> x * star a + x = x
+-- Idempotent addition gives a natural order @a <= b ⟺ a + b == b@. In that order, Kozen's induction laws hold as derived facts:
+--
+-- > a * x + x <= x  ==>  star a * x + x <= x
+-- > x * a + x <= x  ==>  x * star a + x <= x
+--
+-- They are stated here as prose rather than class laws because they involve a partial order and Horn clauses, which do not fit the equational/doctest style of the Conway core.
 class (StarSemiring a, Idempotent a) => KleeneAlgebra a
+
+instance StarSemiring P.Bool where
+  star _ = P.True
+
+instance KleeneAlgebra P.Bool
+
+-- | Conway equations for 'Bool'.
+--
+-- >>> let a = False; b = True in star (a * b) == one + a * star (b * a) * b
+-- True
+--
+-- >>> let a = False; b = True in star (a + b) == star (star a * b) * star a
+-- True
 
 -- | Involutive Ring
 --
diff --git a/src/NumHask/Algebra/Tropical.hs b/src/NumHask/Algebra/Tropical.hs
new file mode 100644
--- /dev/null
+++ b/src/NumHask/Algebra/Tropical.hs
@@ -0,0 +1,65 @@
+-- | Tropical semirings.
+module NumHask.Algebra.Tropical
+  ( MinPlus (..),
+  )
+where
+
+import NumHask.Algebra.Additive (Additive (..))
+import NumHask.Algebra.Group (Idempotent, Magma (..))
+import NumHask.Algebra.Multiplicative (Multiplicative (..))
+import NumHask.Algebra.Ring (KleeneAlgebra, StarSemiring (..))
+import Prelude (Double, Eq, Ord, Show, fromInteger)
+import Prelude qualified as P
+
+-- $setup
+--
+-- >>> :m -Prelude
+-- >>> :set -XRebindableSyntax
+-- >>> import NumHask.Prelude
+-- >>> import NumHask.Algebra.Tropical
+
+-- | The min-plus tropical semiring.
+--
+-- Addition is 'min', multiplication is ordinary addition, the additive unit
+-- is positive infinity, and the multiplicative unit is zero.
+--
+-- >>> MinPlus 3 + MinPlus 2 :: MinPlus Double
+-- MinPlus {getMinPlus = 2.0}
+--
+-- >>> MinPlus 3 * MinPlus 2 :: MinPlus Double
+-- MinPlus {getMinPlus = 5.0}
+newtype MinPlus a = MinPlus
+  { getMinPlus :: a
+  }
+  deriving (Eq, Ord, Show)
+
+instance Additive (MinPlus Double) where
+  MinPlus a + MinPlus b = MinPlus (P.min a b)
+  zero = MinPlus (1 P./ 0)
+
+instance Multiplicative (MinPlus Double) where
+  MinPlus a * MinPlus b = MinPlus (a P.+ b)
+  one = MinPlus 0
+
+instance Magma (MinPlus Double) where
+  a ⊕ b = a + b
+
+instance Idempotent (MinPlus Double)
+
+-- | Star is zero in a min-plus semiring: the cheapest repeated traversal is
+-- to stay put.
+--
+-- >>> star (MinPlus 2 :: MinPlus Double)
+-- MinPlus {getMinPlus = 0.0}
+--
+-- Conway equations for 'MinPlus Double'.
+--
+-- >>> let a = MinPlus 2 :: MinPlus Double; b = MinPlus 3 :: MinPlus Double in star (a * b) == one + a * star (b * a) * b
+-- True
+--
+-- >>> let a = MinPlus 2 :: MinPlus Double; b = MinPlus 3 :: MinPlus Double in star (a + b) == star (star a * b) * star a
+-- True
+instance StarSemiring (MinPlus Double) where
+  star _ = one
+
+instance KleeneAlgebra (MinPlus Double)
diff --git a/src/NumHask/Data/Complex.hs b/src/NumHask/Data/Complex.hs
--- a/src/NumHask/Data/Complex.hs
+++ b/src/NumHask/Data/Complex.hs
@@ -8,6 +8,7 @@
     (+:),
     realPart,
     imagPart,
+    normSquared,
   )
 where
 
@@ -41,14 +42,19 @@
     (/),
   )
 
+-- $setup
+--
+-- >>> import NumHask.Prelude
+-- >>> :m -Prelude
+
 -- | The underlying representation is a newtype-wrapped tuple, compared with the base datatype. This was chosen to facilitate the use of DerivingVia.
 newtype Complex a = Complex {complexPair :: (a, a)}
   deriving stock
     ( Eq,
       Show,
       Read,
-      Data,
       Generic,
+      Data,
       Functor
     )
   deriving
@@ -59,8 +65,8 @@
       Epsilon,
       JoinSemiLattice,
       MeetSemiLattice,
-      BoundedJoinSemiLattice,
-      BoundedMeetSemiLattice,
+      LowerBounded,
+      UpperBounded,
       ExpField
     )
     via (EuclideanPair a)
@@ -68,6 +74,14 @@
 infixl 6 +:
 
 -- | Complex number constructor.
+--
+-- Internally, Complex derives most instances via EuclideanPair. For instance,
+--
+-- >>> sqrt (1.0 +: (-1.0)) :: Complex Double
+-- Complex {complexPair = (1.0986841134678098,-0.45508986056222733)}
+--
+-- >>> sqrt ((-1.0) +: 0.0) :: Complex Double
+-- Complex {complexPair = (6.123233995736766e-17,1.0)}
 (+:) :: a -> a -> Complex a
 (+:) r i = Complex (r, i)
 
@@ -118,3 +132,8 @@
   ceiling (Complex (x, y)) = Complex (ceiling x, ceiling y)
   floor (Complex (x, y)) = Complex (floor x, floor y)
   truncate (Complex (x, y)) = Complex (truncate x, truncate y)
+
+-- | The squared norm: frequently useful, and doesn't require the
+-- ability to take square roots.
+normSquared :: (Distributive a) => Complex a -> a
+normSquared (Complex (x, y)) = x * x + y * y
diff --git a/src/NumHask/Data/Integral.hs b/src/NumHask/Data/Integral.hs
--- a/src/NumHask/Data/Integral.hs
+++ b/src/NumHask/Data/Integral.hs
@@ -10,6 +10,7 @@
     odd,
     (^^),
     (^),
+    (^+),
   )
 where
 
@@ -116,6 +117,18 @@
   rem f f' a = f a `mod` f' a
   quotRem f f' = (\a -> fst (f a `quotRem` f' a), \a -> snd (f a `quotRem` f' a))
 
+-- |
+-- >>> even 2
+-- True
+even :: (P.Eq a, Integral a) => a -> P.Bool
+even n = n `rem` (one + one) P.== zero
+
+-- |
+-- >>> odd 3
+-- True
+odd :: (P.Eq a, Integral a) => a -> P.Bool
+odd = P.not . even
+
 -- | toIntegral is kept separate from Integral to help with compatability issues.
 --
 -- > toIntegral a == a
@@ -409,17 +422,9 @@
 instance FromInteger Word64 where
   fromInteger = P.fromInteger
 
--- |
--- >>> even 2
--- True
-even :: (P.Eq a, Integral a) => a -> P.Bool
-even n = n `rem` (one + one) P.== zero
+deriving instance (FromInteger a) => FromInteger (Sum a)
 
--- |
--- >>> odd 3
--- True
-odd :: (P.Eq a, Integral a) => a -> P.Bool
-odd = P.not . even
+deriving instance (FromInteger a) => FromInteger (Product a)
 
 infixr 8 ^^
 
@@ -464,3 +469,32 @@
 (^) ::
   (Divisive a) => a -> Int -> a
 (^) x n = x ^^ n
+
+infixr 8 ^+
+
+-- | raise a number to a non-negative 'Natural' power.
+--
+-- Unlike '(^^)' and '(^)', this does not require 'Divisive' or 'Subtractive'
+-- constraints, so it works for unsigned types such as 'Natural' and 'Word64'.
+--
+-- >>> 2 ^+ 3
+-- 8
+--
+-- >>> 2 ^+ 0
+-- 1
+(^+) ::
+  (Multiplicative a) => a -> Natural -> a
+x0 ^+ y0 =
+  case compare y0 zero of
+    EQ -> one
+    GT -> f x0 y0
+    LT -> P.error "(^+): negative exponent"
+  where
+    f x y
+      | even y = f (x * x) (y `quot` two)
+      | y P.== one = x
+      | P.otherwise = g (x * x) (y `quot` two) x
+    g x y z
+      | even y = g (x * x) (y `quot` two) z
+      | y P.== one = x * z
+      | P.otherwise = g (x * x) (y `quot` two) (x * z)
diff --git a/src/NumHask/Data/Positive.hs b/src/NumHask/Data/Positive.hs
--- a/src/NumHask/Data/Positive.hs
+++ b/src/NumHask/Data/Positive.hs
@@ -28,6 +28,7 @@
 import NumHask.Data.Integral
 import NumHask.Data.Rational
 import NumHask.Data.Wrapped
+import Numeric.Natural (Natural, minusNaturalMaybe)
 import Prelude (Eq, Ord, Show)
 import Prelude qualified as P
 
@@ -84,11 +85,11 @@
       DivisiveAction,
       JoinSemiLattice,
       MeetSemiLattice,
-      BoundedMeetSemiLattice
+      UpperBounded
     )
     via (Wrapped a)
 
-instance (MeetSemiLattice a, Integral a) => FromIntegral (Positive a) a where
+instance (JoinSemiLattice a, Integral a) => FromIntegral (Positive a) a where
   fromIntegral a = positive a
 
 instance (FromIntegral a b) => FromIntegral (Positive a) b where
@@ -103,7 +104,7 @@
 instance (ToRatio a b) => ToRatio (Positive a) b where
   toRatio (UnsafePositive a) = toRatio a
 
-instance (Additive a, JoinSemiLattice a) => BoundedJoinSemiLattice (Positive a) where
+instance (Additive a, JoinSemiLattice a) => LowerBounded (Positive a) where
   bottom = UnsafePositive zero
 
 instance QuotientField (Positive P.Double) where
@@ -124,8 +125,8 @@
 --
 -- >>> positive (-1)
 -- UnsafePositive {unPositive = 0}
-positive :: (Additive a, MeetSemiLattice a) => a -> Positive a
-positive a = UnsafePositive (a /\ zero)
+positive :: (Additive a, JoinSemiLattice a) => a -> Positive a
+positive a = UnsafePositive (a \/ zero)
 
 -- | Unsafe constructor.
 --
@@ -139,9 +140,9 @@
 -- >>> maybePositive (-one)
 -- Nothing
 maybePositive :: (Additive a, MeetSemiLattice a) => a -> Maybe (Positive a)
-maybePositive a = bool Nothing (Just (UnsafePositive a)) (a `meetLeq` zero)
+maybePositive a = bool Nothing (Just (UnsafePositive a)) (zero `meetLeq` a)
 
-instance (Subtractive a, MeetSemiLattice a) => Monus (Positive a) where
+instance (Subtractive a, JoinSemiLattice a) => Monus (Positive a) where
   (UnsafePositive a) ∸ (UnsafePositive b) = positive (a - b)
 
 -- | A field but with truncated subtraction.
@@ -161,9 +162,26 @@
 
   infixl 6 ∸
   (∸) :: a -> a -> a
-  default (∸) :: (BoundedJoinSemiLattice a, MeetSemiLattice a, Subtractive a) => a -> a -> a
-  a ∸ b = bottom /\ (a - b)
+  default (∸) :: (LowerBounded a, Subtractive a) => a -> a -> a
+  a ∸ b = bottom \/ (a - b)
 
+-- | A newtype wrapper intended for defining Monus instances by:
+-- "x ∸ y = if x < y then zero else x - y"
+newtype MonusFromOrd a = MonusFromOrd a
+  deriving (Eq, Ord, Additive, Subtractive)
+
+instance (Ord a, Subtractive a) => Monus (MonusFromOrd a) where
+  x ∸ y
+    | x P.< y = zero
+    | P.otherwise = x - y
+
+-- | It appears that Haskell doesn't have any built in truncated
+-- subtraction operation for Word
+deriving via MonusFromOrd P.Word instance Monus P.Word
+
+instance Monus Natural where
+  x ∸ y = fromMaybe 0 (minusNaturalMaybe x y)
+
 -- | Truncated addition
 --
 -- @since 0.12
@@ -171,5 +189,5 @@
   {-# MINIMAL (∔) #-}
   infixl 6 ∔
   (∔) :: a -> a -> a
-  default (∔) :: (BoundedMeetSemiLattice a, JoinSemiLattice a, Additive a) => a -> a -> a
+  default (∔) :: (UpperBounded a, JoinSemiLattice a, Additive a) => a -> a -> a
   a ∔ b = top \/ (a + b)
diff --git a/src/NumHask/Data/Rational.hs b/src/NumHask/Data/Rational.hs
--- a/src/NumHask/Data/Rational.hs
+++ b/src/NumHask/Data/Rational.hs
@@ -10,6 +10,8 @@
     FromRational (..),
     reduce,
     gcd,
+    numerator,
+    denominator,
   )
 where
 
@@ -41,7 +43,13 @@
 -- | Ratio of two integers
 type Rational = Ratio Integer
 
-instance (P.Eq a, Subtractive a, EndoBased a, Absolute a, Integral a) => P.Eq (Ratio a) where
+numerator :: Ratio a -> a
+numerator (a :% _) = a
+
+denominator :: Ratio a -> a
+denominator (_ :% a) = a
+
+instance (P.Eq a, P.Ord a, Subtractive a, EndoBased a, Absolute a, Integral a) => P.Eq (Ratio a) where
   a@(xa :% ya) == b@(xb :% yb)
     | isRNaN a P.|| isRNaN b = P.False
     | xa == zero P.&& xb == zero = P.True
@@ -82,7 +90,7 @@
   Divisive (Ratio a)
   where
   recip (x :% y)
-    | signum x P.== negate one = negate y :% negate x
+    | x P.< zero = negate y :% negate x
     | P.otherwise = y :% x
 
 instance (P.Ord a, EndoBased a, Absolute a, ToInt a, Integral a, Ring a) => QuotientField (Ratio a) where
@@ -100,13 +108,16 @@
   magnitude (n :% d) = abs n :% abs d
 
 instance (P.Ord a, Integral a, EndoBased a, Subtractive a) => JoinSemiLattice (Ratio a) where
-  (\/) = P.min
+  (\/) = P.max
 
 instance (P.Ord a, Integral a, EndoBased a, Subtractive a) => MeetSemiLattice (Ratio a) where
-  (/\) = P.max
+  (/\) = P.min
 
-instance (P.Ord a, EndoBased a, Integral a, Ring a, MeetSemiLattice a) => Epsilon (Ratio a)
+instance (P.Ord a, EndoBased a, Integral a, Ring a) => Epsilon (Ratio a)
 
+instance (FromInteger a, Multiplicative a) => FromInteger (Ratio a) where
+  fromInteger x = fromInteger x :% one
+
 instance (FromIntegral a b, Multiplicative a) => FromIntegral (Ratio a) b where
   fromIntegral x = fromIntegral x :% one
 
@@ -208,7 +219,7 @@
 --
 -- prop> \a b -> reduce a b == a :% b || b == zero
 reduce ::
-  (P.Eq a, Subtractive a, EndoBased a, Integral a) => a -> a -> Ratio a
+  (P.Ord a, Subtractive a, EndoBased a, Integral a) => a -> a -> Ratio a
 reduce x y
   | x P.== zero P.&& y P.== zero = zero :% zero
   | z P.== zero = one :% zero
@@ -216,7 +227,7 @@
   where
     z = gcd x y
     n % d
-      | signum d P.== negate one = negate n :% negate d
+      | d P.< zero = negate n :% negate d
       | P.otherwise = n :% d
 
 -- | @'gcd' x y@ is the non-negative factor of both @x@ and @y@ of which
diff --git a/src/NumHask/Data/Wrapped.hs b/src/NumHask/Data/Wrapped.hs
--- a/src/NumHask/Data/Wrapped.hs
+++ b/src/NumHask/Data/Wrapped.hs
@@ -37,8 +37,8 @@
       FromRational,
       MeetSemiLattice,
       JoinSemiLattice,
-      BoundedJoinSemiLattice,
-      BoundedMeetSemiLattice,
+      LowerBounded,
+      UpperBounded,
       Basis,
       Direction,
       Epsilon,
diff --git a/src/NumHask/Exception.hs b/src/NumHask/Exception.hs
--- a/src/NumHask/Exception.hs
+++ b/src/NumHask/Exception.hs
@@ -6,11 +6,10 @@
 where
 
 import Control.Exception
-import Data.Typeable (Typeable)
 import Prelude qualified as P
 
 -- | A numhask exception.
 newtype NumHaskException = NumHaskException {errorMessage :: P.String}
-  deriving (P.Show, Typeable)
+  deriving (P.Show)
 
 instance Exception NumHaskException
diff --git a/test/doctests.hs b/test/doctests.hs
deleted file mode 100644
--- a/test/doctests.hs
+++ /dev/null
@@ -1,8 +0,0 @@
-module Main where
-
-import System.Environment (getArgs)
-import Test.DocTest (mainFromCabal)
-import Prelude (IO, (=<<))
-
-main :: IO ()
-main = mainFromCabal "numhask" =<< getArgs
