diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,16 @@
 # Revision history for heyting-algebra
 
-## 0.0.1.0 -- YYYY-mm-dd
+## 0.0.2.0
+
+* Added Algebra.Heyting.CounterExample
+* Added Algebra.Heyting.Free.atom
+* Added `BoolRing` a Boolean ring
+* Check distributivity laws
+* newtype `Ordered` adds Heyting algebra instance for every type satisfying the
+  `Ord` constraint.
+* (<=>) operator added
+* Library does not depens on QuickCheck anymore
+
+## 0.0.1.1 -- 2018.10.5
 
 * First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,373 +1,22 @@
-Mozilla Public License Version 2.0
-==================================
-
-1. Definitions
---------------
-
-1.1. "Contributor"
-    means each individual or legal entity that creates, contributes to
-    the creation of, or owns Covered Software.
-
-1.2. "Contributor Version"
-    means the combination of the Contributions of others (if any) used
-    by a Contributor and that particular Contributor's Contribution.
-
-1.3. "Contribution"
-    means Covered Software of a particular Contributor.
-
-1.4. "Covered Software"
-    means Source Code Form to which the initial Contributor has attached
-    the notice in Exhibit A, the Executable Form of such Source Code
-    Form, and Modifications of such Source Code Form, in each case
-    including portions thereof.
-
-1.5. "Incompatible With Secondary Licenses"
-    means
-
-    (a) that the initial Contributor has attached the notice described
-        in Exhibit B to the Covered Software; or
-
-    (b) that the Covered Software was made available under the terms of
-        version 1.1 or earlier of the License, but not also under the
-        terms of a Secondary License.
-
-1.6. "Executable Form"
-    means any form of the work other than Source Code Form.
-
-1.7. "Larger Work"
-    means a work that combines Covered Software with other material, in
-    a separate file or files, that is not Covered Software.
-
-1.8. "License"
-    means this document.
-
-1.9. "Licensable"
-    means having the right to grant, to the maximum extent possible,
-    whether at the time of the initial grant or subsequently, any and
-    all of the rights conveyed by this License.
-
-1.10. "Modifications"
-    means any of the following:
-
-    (a) any file in Source Code Form that results from an addition to,
-        deletion from, or modification of the contents of Covered
-        Software; or
-
-    (b) any new file in Source Code Form that contains any Covered
-        Software.
-
-1.11. "Patent Claims" of a Contributor
-    means any patent claim(s), including without limitation, method,
-    process, and apparatus claims, in any patent Licensable by such
-    Contributor that would be infringed, but for the grant of the
-    License, by the making, using, selling, offering for sale, having
-    made, import, or transfer of either its Contributions or its
-    Contributor Version.
-
-1.12. "Secondary License"
-    means either the GNU General Public License, Version 2.0, the GNU
-    Lesser General Public License, Version 2.1, the GNU Affero General
-    Public License, Version 3.0, or any later versions of those
-    licenses.
-
-1.13. "Source Code Form"
-    means the form of the work preferred for making modifications.
-
-1.14. "You" (or "Your")
-    means an individual or a legal entity exercising rights under this
-    License. For legal entities, "You" includes any entity that
-    controls, is controlled by, or is under common control with You. For
-    purposes of this definition, "control" means (a) the power, direct
-    or indirect, to cause the direction or management of such entity,
-    whether by contract or otherwise, or (b) ownership of more than
-    fifty percent (50%) of the outstanding shares or beneficial
-    ownership of such entity.
-
-2. License Grants and Conditions
---------------------------------
-
-2.1. Grants
-
-Each Contributor hereby grants You a world-wide, royalty-free,
-non-exclusive license:
-
-(a) under intellectual property rights (other than patent or trademark)
-    Licensable by such Contributor to use, reproduce, make available,
-    modify, display, perform, distribute, and otherwise exploit its
-    Contributions, either on an unmodified basis, with Modifications, or
-    as part of a Larger Work; and
-
-(b) under Patent Claims of such Contributor to make, use, sell, offer
-    for sale, have made, import, and otherwise transfer either its
-    Contributions or its Contributor Version.
-
-2.2. Effective Date
-
-The licenses granted in Section 2.1 with respect to any Contribution
-become effective for each Contribution on the date the Contributor first
-distributes such Contribution.
-
-2.3. Limitations on Grant Scope
-
-The licenses granted in this Section 2 are the only rights granted under
-this License. No additional rights or licenses will be implied from the
-distribution or licensing of Covered Software under this License.
-Notwithstanding Section 2.1(b) above, no patent license is granted by a
-Contributor:
-
-(a) for any code that a Contributor has removed from Covered Software;
-    or
-
-(b) for infringements caused by: (i) Your and any other third party's
-    modifications of Covered Software, or (ii) the combination of its
-    Contributions with other software (except as part of its Contributor
-    Version); or
-
-(c) under Patent Claims infringed by Covered Software in the absence of
-    its Contributions.
-
-This License does not grant any rights in the trademarks, service marks,
-or logos of any Contributor (except as may be necessary to comply with
-the notice requirements in Section 3.4).
-
-2.4. Subsequent Licenses
-
-No Contributor makes additional grants as a result of Your choice to
-distribute the Covered Software under a subsequent version of this
-License (see Section 10.2) or under the terms of a Secondary License (if
-permitted under the terms of Section 3.3).
-
-2.5. Representation
-
-Each Contributor represents that the Contributor believes its
-Contributions are its original creation(s) or it has sufficient rights
-to grant the rights to its Contributions conveyed by this License.
-
-2.6. Fair Use
-
-This License is not intended to limit any rights You have under
-applicable copyright doctrines of fair use, fair dealing, or other
-equivalents.
-
-2.7. Conditions
-
-Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
-in Section 2.1.
-
-3. Responsibilities
--------------------
-
-3.1. Distribution of Source Form
-
-All distribution of Covered Software in Source Code Form, including any
-Modifications that You create or to which You contribute, must be under
-the terms of this License. You must inform recipients that the Source
-Code Form of the Covered Software is governed by the terms of this
-License, and how they can obtain a copy of this License. You may not
-attempt to alter or restrict the recipients' rights in the Source Code
-Form.
-
-3.2. Distribution of Executable Form
-
-If You distribute Covered Software in Executable Form then:
-
-(a) such Covered Software must also be made available in Source Code
-    Form, as described in Section 3.1, and You must inform recipients of
-    the Executable Form how they can obtain a copy of such Source Code
-    Form by reasonable means in a timely manner, at a charge no more
-    than the cost of distribution to the recipient; and
-
-(b) You may distribute such Executable Form under the terms of this
-    License, or sublicense it under different terms, provided that the
-    license for the Executable Form does not attempt to limit or alter
-    the recipients' rights in the Source Code Form under this License.
-
-3.3. Distribution of a Larger Work
-
-You may create and distribute a Larger Work under terms of Your choice,
-provided that You also comply with the requirements of this License for
-the Covered Software. If the Larger Work is a combination of Covered
-Software with a work governed by one or more Secondary Licenses, and the
-Covered Software is not Incompatible With Secondary Licenses, this
-License permits You to additionally distribute such Covered Software
-under the terms of such Secondary License(s), so that the recipient of
-the Larger Work may, at their option, further distribute the Covered
-Software under the terms of either this License or such Secondary
-License(s).
-
-3.4. Notices
-
-You may not remove or alter the substance of any license notices
-(including copyright notices, patent notices, disclaimers of warranty,
-or limitations of liability) contained within the Source Code Form of
-the Covered Software, except that You may alter any license notices to
-the extent required to remedy known factual inaccuracies.
-
-3.5. Application of Additional Terms
-
-You may choose to offer, and to charge a fee for, warranty, support,
-indemnity or liability obligations to one or more recipients of Covered
-Software. However, You may do so only on Your own behalf, and not on
-behalf of any Contributor. You must make it absolutely clear that any
-such warranty, support, indemnity, or liability obligation is offered by
-You alone, and You hereby agree to indemnify every Contributor for any
-liability incurred by such Contributor as a result of warranty, support,
-indemnity or liability terms You offer. You may include additional
-disclaimers of warranty and limitations of liability specific to any
-jurisdiction.
-
-4. Inability to Comply Due to Statute or Regulation
----------------------------------------------------
-
-If it is impossible for You to comply with any of the terms of this
-License with respect to some or all of the Covered Software due to
-statute, judicial order, or regulation then You must: (a) comply with
-the terms of this License to the maximum extent possible; and (b)
-describe the limitations and the code they affect. Such description must
-be placed in a text file included with all distributions of the Covered
-Software under this License. Except to the extent prohibited by statute
-or regulation, such description must be sufficiently detailed for a
-recipient of ordinary skill to be able to understand it.
-
-5. Termination
---------------
-
-5.1. The rights granted under this License will terminate automatically
-if You fail to comply with any of its terms. However, if You become
-compliant, then the rights granted under this License from a particular
-Contributor are reinstated (a) provisionally, unless and until such
-Contributor explicitly and finally terminates Your grants, and (b) on an
-ongoing basis, if such Contributor fails to notify You of the
-non-compliance by some reasonable means prior to 60 days after You have
-come back into compliance. Moreover, Your grants from a particular
-Contributor are reinstated on an ongoing basis if such Contributor
-notifies You of the non-compliance by some reasonable means, this is the
-first time You have received notice of non-compliance with this License
-from such Contributor, and You become compliant prior to 30 days after
-Your receipt of the notice.
-
-5.2. If You initiate litigation against any entity by asserting a patent
-infringement claim (excluding declaratory judgment actions,
-counter-claims, and cross-claims) alleging that a Contributor Version
-directly or indirectly infringes any patent, then the rights granted to
-You by any and all Contributors for the Covered Software under Section
-2.1 of this License shall terminate.
-
-5.3. In the event of termination under Sections 5.1 or 5.2 above, all
-end user license agreements (excluding distributors and resellers) which
-have been validly granted by You or Your distributors under this License
-prior to termination shall survive termination.
-
-************************************************************************
-*                                                                      *
-*  6. Disclaimer of Warranty                                           *
-*  -------------------------                                           *
-*                                                                      *
-*  Covered Software is provided under this License on an "as is"       *
-*  basis, without warranty of any kind, either expressed, implied, or  *
-*  statutory, including, without limitation, warranties that the       *
-*  Covered Software is free of defects, merchantable, fit for a        *
-*  particular purpose or non-infringing. The entire risk as to the     *
-*  quality and performance of the Covered Software is with You.        *
-*  Should any Covered Software prove defective in any respect, You     *
-*  (not any Contributor) assume the cost of any necessary servicing,   *
-*  repair, or correction. This disclaimer of warranty constitutes an   *
-*  essential part of this License. No use of any Covered Software is   *
-*  authorized under this License except under this disclaimer.         *
-*                                                                      *
-************************************************************************
-
-************************************************************************
-*                                                                      *
-*  7. Limitation of Liability                                          *
-*  --------------------------                                          *
-*                                                                      *
-*  Under no circumstances and under no legal theory, whether tort      *
-*  (including negligence), contract, or otherwise, shall any           *
-*  Contributor, or anyone who distributes Covered Software as          *
-*  permitted above, be liable to You for any direct, indirect,         *
-*  special, incidental, or consequential damages of any character      *
-*  including, without limitation, damages for lost profits, loss of    *
-*  goodwill, work stoppage, computer failure or malfunction, or any    *
-*  and all other commercial damages or losses, even if such party      *
-*  shall have been informed of the possibility of such damages. This   *
-*  limitation of liability shall not apply to liability for death or   *
-*  personal injury resulting from such party's negligence to the       *
-*  extent applicable law prohibits such limitation. Some               *
-*  jurisdictions do not allow the exclusion or limitation of           *
-*  incidental or consequential damages, so this exclusion and          *
-*  limitation may not apply to You.                                    *
-*                                                                      *
-************************************************************************
-
-8. Litigation
--------------
-
-Any litigation relating to this License may be brought only in the
-courts of a jurisdiction where the defendant maintains its principal
-place of business and such litigation shall be governed by laws of that
-jurisdiction, without reference to its conflict-of-law provisions.
-Nothing in this Section shall prevent a party's ability to bring
-cross-claims or counter-claims.
-
-9. Miscellaneous
-----------------
-
-This License represents the complete agreement concerning the subject
-matter hereof. If any provision of this License is held to be
-unenforceable, such provision shall be reformed only to the extent
-necessary to make it enforceable. Any law or regulation which provides
-that the language of a contract shall be construed against the drafter
-shall not be used to construe this License against a Contributor.
-
-10. Versions of the License
----------------------------
-
-10.1. New Versions
-
-Mozilla Foundation is the license steward. Except as provided in Section
-10.3, no one other than the license steward has the right to modify or
-publish new versions of this License. Each version will be given a
-distinguishing version number.
-
-10.2. Effect of New Versions
-
-You may distribute the Covered Software under the terms of the version
-of the License under which You originally received the Covered Software,
-or under the terms of any subsequent version published by the license
-steward.
-
-10.3. Modified Versions
-
-If you create software not governed by this License, and you want to
-create a new license for such software, you may create and use a
-modified version of this License if you rename the license and remove
-any references to the name of the license steward (except to note that
-such modified license differs from this License).
-
-10.4. Distributing Source Code Form that is Incompatible With Secondary
-Licenses
-
-If You choose to distribute Source Code Form that is Incompatible With
-Secondary Licenses under the terms of this version of the License, the
-notice described in Exhibit B of this License must be attached.
-
-Exhibit A - Source Code Form License Notice
--------------------------------------------
-
-  This Source Code Form is subject to the terms of the Mozilla Public
-  License, v. 2.0. If a copy of the MPL was not distributed with this
-  file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-If it is not possible or desirable to put the notice in a particular
-file, then You may include the notice in a location (such as a LICENSE
-file in a relevant directory) where a recipient would be likely to look
-for such a notice.
+Copyright (c) 2018 Marcin Szamotulski
+All rights reserved.
 
-You may add additional accurate notices of copyright ownership.
+Redistribution and use in source and binary forms, with or without modification, are permitted
+provided that the following conditions are met:
 
-Exhibit B - "Incompatible With Secondary Licenses" Notice
----------------------------------------------------------
+    * Redistributions of source code must retain the above copyright notice, this list of
+      conditions and the following disclaimer.
+    * 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.
+    * Neither the name of Maximilian Bolingbroke nor the names of other contributors may be used to
+      endorse or promote products derived from this software without specific prior written permission.
 
-  This Source Code Form is "Incompatible With Secondary Licenses", as
-  defined by the Mozilla Public License, v. 2.0.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -7,30 +7,46 @@
 [lattices](https://hackage.haskell.org/package/lattices) and
 [free-algebras](https://hackage.haskell.org/package/free-algebras) (to provide
 combinators for free Heyting algebras).  The package also defines a type class
-for Boolean algebras and comes with a handful of instances.
+for Boolean algebras and comes with many useful instances.
 
+A note about notation: this package is based on
+[lattices](https://hackage.haskell.org/package/lattices), and both are using
+notation and names common in lattice theory and logic.  Where `&&` becomes `∧`
+and is called `meet` and `||` is denoted by `∨` and is usually called
+`join`.  The `lattice` package provides `\/` and `/\` operators as well as type
+classes for various flavors of posets and lattices.
+
 A very good introduction to Heyting algebras can be found at
 [ncatlab](https://ncatlab.org/nlab/show/Heyting%2Balgebra).  Heyting algebras
 are the crux of [intuitionistic
 logic](https://en.wikipedia.org/wiki/Intuitionistic_logic), which drops the
-axiom of exluded middle.  From categorical point of view, Heyting algebras are
+axiom of excluded middle.  From categorical point of view, Heyting algebras are
 posets (categories with at most one arrow between any objects), which are also
 Cartesian closed (and finitely (co-)complete).  Note that this makes any
 Heyting algebra a simply typed lambda calculus; hence one more incentive to
-learn how to use them.
+learn about them.  For example currying holds in every Heyting algebra:
+`a => (b ⇒ c)` is equal to `(a ∧ b) ⇒ c`
 
 The most important operation is implication `(==>) :: HeytingAlgebra a => a ->
-a -> a`; since every Boolean algebra is a Heyting algebra via `a ==>
-b = not a \/ b` (using the lattice notation for `or`).  It is very handy in
-expression conditional logic.
+a -> a` (which we might also write as ⇒ in documentation).  Every Boolean
+algebra is a Heyting algebra via `a ==> b = not a \/ b` (using the lattice
+notation for `or`).  It is very handy in expression conditional logic.
 
-Some basic examples of Heyting algebras:
+Some examples of Heyting algebras:
 * `Bool` is a Boolean algebra
 * `(Ord a, Bounded a) => a`; the implication is defined as: if `a ≤ b` then `a
-  ⇒ b = maxBound`; otherwise `a ⇒ b = b`; e.g. integers with both `±∞` (it can
-  be represented by `Levitated Int`.  This type is not a Boolean algebra.
+  ⇒ b = maxBound`, otherwise `a ⇒ b = b`; e.g. integers with both `±∞` (it can
+  be represented by `Levitated Int`.  Note that it is not a Boolean algebra.
 * The power set is a Boolean algebra, in Haskell it can be represented by `Set
   a` (one might need to require `a` to be finite though, otherwise `not (not
-  empty)` might be `undefined` rather than `empty`).
+  empty)` might be `undefined` rather than `empty`).  It is a well known fact
+  that every Boolean algebra is isomorphic to a power set.
+* ```haskell
+    type CounterExample a = Lifted (Op (Set a))
+  ```
+  is a Heyting algebra; it is useful for gathering counter examples in
+  a similar way that `Property` from `QuickCheck` library does (put pure).
+  This library provides some useful functions for this type, see the
+  `Algebra.Heyting.Properties` and tests for example usage.
 * More generally every type `(Ord k, Finite k, HeytingAlgebra v) => Map k a` is
   a Heyting algebra (though in general not a Boolean one).
diff --git a/heyting-algebras.cabal b/heyting-algebras.cabal
--- a/heyting-algebras.cabal
+++ b/heyting-algebras.cabal
@@ -1,42 +1,38 @@
 name:                heyting-algebras
-version:             0.0.1.2
+version:             0.0.2.0
 synopsis:            Heyting and Boolean algebras
 description:
   This package provides Heyting and Boolean operations together
   with various constructions of Heyting algebras.
-license:             MPL-2.0
+license:             BSD3
 license-file:        LICENSE
 author:              Marcin Szamotulski
 maintainer:          profunctor@pm.me
-copyright:           (c) 2018 Marcin Szamotulski
+copyright:           (c) 2018-2019 Marcin Szamotulski
 category:            Math
 build-type:          Simple
 extra-source-files:
   ChangeLog.md
   README.md
 cabal-version:       >=1.10
-tested-with:         GHC==8.2.2, GHC==8.4.3
-
-flag export-properties
-  description:
-    Export quickcheck properties from library; this adds QuickCheck
-    as a dependency.
-  manual: False
-  default: True
+tested-with:         GHC==8.0.2, GHC==8.2.2, GHC==8.4.4, GHC==8.6.3
 
 library
   exposed-modules:     Algebra.Heyting
                        Algebra.Heyting.Free
                        Algebra.Heyting.Layered
+                       Algebra.Heyting.BoolRing
+                       Algebra.Heyting.CounterExample
+                       Algebra.Heyting.Properties
                        Algebra.Boolean
                        Algebra.Boolean.Free
-  -- other-modules:
-  -- other-extensions:
+                       Algebra.Boolean.Properties
   build-depends:       base           >= 4.9      && < 4.13
                      , containers     >= 0.4.2    && < 0.7
-                     , free-algebras  >= 0.0.4    && < 0.0.6    
+                     , free-algebras  >= 0.0.4    && < 0.0.8
                      , hashable       >= 1.2.6.1  && < 1.3
-                     , lattices       >= 1.0      && < 1.11
+                     , lattices       >= 1.0      && < 1.8
+                     , semiring-simple >= 1.0     && < 1.2
                      , tagged         >= 0.8.5    && < 0.9
                      , unordered-containers
                                       >= 0.2.6.0  && < 0.3
@@ -45,14 +41,10 @@
   default-language:    Haskell2010
   default-extensions:  FlexibleInstances
                        RankNTypes
+                       PackageImports
   ghc-options:        -Wall
-  if flag(export-properties)
-    build-depends:
-      QuickCheck     >= 2.10     && < 2.13
-    cpp-options:
-      -DEXPORT_PROPERTIES
 
-test-suite heyting-algebras-test
+test-suite tests
   type:                exitcode-stdio-1.0
   hs-source-dirs:      test
   main-is:             Main.hs
@@ -67,6 +59,7 @@
   default-language:    Haskell2010
   default-extensions:  FlexibleInstances
                      , TypeApplications
+  ghc-options:       -threaded -rtsopts -with-rtsopts=-N
 
 source-repository head
   type:     git
diff --git a/src/Algebra/Boolean.hs b/src/Algebra/Boolean.hs
--- a/src/Algebra/Boolean.hs
+++ b/src/Algebra/Boolean.hs
@@ -12,65 +12,28 @@
   , Boolean
   , runBoolean
   , boolean
-
-    -- * Properties
-    -- $properties
-  , prop_not 
-  , prop_BooleanAlgebra
   ) where
 
-import Prelude hiding (not)
-
-import Control.Applicative    (Const (..))
-import Data.Data              (Data, Typeable)
-import Data.Functor.Identity  (Identity (..))
-import Data.Proxy             (Proxy (..))
-import Data.Semigroup         (All (..), Any (..), Endo (..))
-import Data.Tagged            (Tagged (..))
-import Data.Universe.Class    (Finite)
-import qualified Data.Set as S
-import GHC.Generics          (Generic)
-#ifdef EXPORT_PROPERTIES
-import Test.QuickCheck hiding ((==>))
-#endif
+import           Prelude hiding (not)
 
-import Algebra.Lattice ( Lattice
-                       , BoundedLattice
-                       , JoinSemiLattice (..)
-                       , BoundedJoinSemiLattice
-                       , MeetSemiLattice (..)
-                       , BoundedMeetSemiLattice
-                       , bottom
-                       , top
-                       )
+import           Data.Data              (Data, Typeable)
+import           GHC.Generics           (Generic)
 
-import Algebra.Heyting ( HeytingAlgebra (..)
-                       , iff
-                       , iff'
-                       , not
-                       , toBoolean
-                       , prop_HeytingAlgebra
-                       )
+import           Algebra.Lattice        ( Lattice
+                                        , BoundedLattice
+                                        , JoinSemiLattice (..)
+                                        , BoundedJoinSemiLattice
+                                        , MeetSemiLattice (..)
+                                        , BoundedMeetSemiLattice
+                                        )
 
--- |
--- Boolean algebra is a Heyting algebra which negation satisfies the law of
--- excluded middle, i.e. either of the following:
---
--- prop> not . not == not
---
--- or
---
--- prop> x ∨ not x == top
---
--- Another characterisation of Boolean algebras is as
--- [complemented](https://en.wikipedia.org/wiki/Complemented_lattice)
--- [distributive lattices](https://ncatlab.org/nlab/show/distributive+lattice)
--- where the complement satisfies the following three properties:
---
--- prop> (not a) ∧ a == bottom and (not a) ∨ a == top -- excluded middle law
--- prop> not (not a) == a                             -- involution law
--- prop> a ≤ b ⇒ not b ≤ not a                        -- order-reversing
-class HeytingAlgebra a => BooleanAlgebra a
+import           Algebra.Heyting        ( HeytingAlgebra (..)
+                                        , BooleanAlgebra
+                                        , iff
+                                        , iff'
+                                        , not
+                                        , toBoolean
+                                        )
 
 -- |
 -- @'Boolean'@ is the left adjoint functor from the category of Heyting algebras
@@ -86,68 +49,8 @@
 
 instance HeytingAlgebra a => BooleanAlgebra (Boolean a)
 
--- TODO: move to tests
-instance (Arbitrary a, HeytingAlgebra a) => Arbitrary (Boolean a) where
-  arbitrary = boolean <$> arbitrary
-  shrink (Boolean a) = [ boolean a' | a' <- shrink a ]
-
 -- |
 -- Smart constructro of the @'Boolean'@ type.
 boolean :: HeytingAlgebra a => a -> Boolean a
 boolean = Boolean . toBoolean
 
---
--- Instances
---
-
-instance BooleanAlgebra Bool
-
-instance BooleanAlgebra All
-
-instance BooleanAlgebra Any
-
-instance BooleanAlgebra ()
-
-instance BooleanAlgebra (Proxy a)
-
-instance BooleanAlgebra a => BooleanAlgebra (Tagged t a)
-
-instance BooleanAlgebra b => BooleanAlgebra (a -> b)
-
-instance BooleanAlgebra a => BooleanAlgebra (Identity a)
-
-instance BooleanAlgebra a => BooleanAlgebra (Const a b)
-
-instance BooleanAlgebra a => BooleanAlgebra (Endo a)
-
-instance (BooleanAlgebra a, BooleanAlgebra b) => BooleanAlgebra (a, b)
-
---
--- containers
---
-
-instance (Ord a, Finite a) => BooleanAlgebra (S.Set a)
-
--- 
--- $properties
---
--- /Properties are exported only if @export-properties@ cabal flag is defined./
-#ifdef EXPORT_PROPERTIES
-
--- |
--- Test that @'not'@ satisfies Boolean algebra axioms.
-prop_not :: (HeytingAlgebra a, Eq a, Show a) => a -> Property
-prop_not a =
-       counterexample "not (not a) /= a" (not (not a) === a)
-  .&&. counterexample "not a ∧ a /= bottom" (not a /\ a === bottom)
-  .&&. counterexample "not a ∨ a /= top" (not a \/ a === top)
-
--- |
--- Test that @a@ is satisfy both @'Algebra.Heyting.prop_HeytingAlgebra'@ and
--- @'prop_not'@.
-prop_BooleanAlgebra :: (BooleanAlgebra a, Eq a, Show a)
-                    => a -> a -> a -> Property
-prop_BooleanAlgebra a b c =
-       prop_HeytingAlgebra a b c
-  .&&. prop_not a
-#endif
diff --git a/src/Algebra/Boolean/Free.hs b/src/Algebra/Boolean/Free.hs
--- a/src/Algebra/Boolean/Free.hs
+++ b/src/Algebra/Boolean/Free.hs
@@ -3,26 +3,24 @@
   ( FreeBoolean (..)
   ) where
 
-import Control.Monad (ap)
-import Algebra.Lattice
-  ( BoundedJoinSemiLattice (..)
-  , JoinSemiLattice (..)
-  , BoundedMeetSemiLattice (..)
-  , MeetSemiLattice (..)
-  , BoundedLattice
-  , Lattice
-  )
-import Data.Algebra.Free
-  ( AlgebraType0
-  , AlgebraType
-  , FreeAlgebra (..)
-  , proof
-  , fmapFree
-  , bindFree
-  )
+import           Control.Monad     (ap)
+import           Algebra.Lattice   ( BoundedJoinSemiLattice (..)
+                                   , JoinSemiLattice (..)
+                                   , BoundedMeetSemiLattice (..)
+                                   , MeetSemiLattice (..)
+                                   , BoundedLattice
+                                   , Lattice
+                                   )
+import           Data.Algebra.Free ( AlgebraType0
+                                   , AlgebraType
+                                   , FreeAlgebra (..)
+                                   , proof
+                                   , fmapFree
+                                   , bindFree
+                                   )
 
-import Algebra.Boolean (BooleanAlgebra)
-import Algebra.Heyting (HeytingAlgebra (..))
+import           Algebra.Boolean   (BooleanAlgebra)
+import           Algebra.Heyting   (HeytingAlgebra (..))
 
 -- |
 -- Free Boolean algebra.  @'FreeAlgebra'@ instance provides all the usual
diff --git a/src/Algebra/Boolean/Properties.hs b/src/Algebra/Boolean/Properties.hs
new file mode 100644
--- /dev/null
+++ b/src/Algebra/Boolean/Properties.hs
@@ -0,0 +1,36 @@
+module Algebra.Boolean.Properties where
+
+import           Prelude hiding (not)
+
+import           Algebra.Lattice (bottom, top, (/\), (\/))
+import           Algebra.Boolean
+import           Algebra.Heyting
+import           Algebra.Heyting.CounterExample ( CounterExample
+                                                , annotate
+                                                , fmapCounterExample
+                                                , (===)
+                                                )
+import           Algebra.Heyting.Properties
+
+-- |
+-- Test that @'not'@ satisfies Boolean algebra axioms.
+prop_not :: (HeytingAlgebra a, Ord a, Eq a, Ord e) => a -> CounterExample e
+prop_not a =
+     (not (not a) === a)
+  /\ (not a /\ a === bottom)
+  /\ (not a \/ a === top)
+
+data BooleanAlgebraLawViolation a
+  = BALVHeytingAlgebraLawViolation (HeytingAlgebraLawViolation a)
+  | BALVNotLawViolation a
+  deriving (Eq, Ord, Show)
+
+-- |
+-- Test that @a@ is satisfy both @'Algebra.Heyting.prop_HeytingAlgebra'@ and
+-- @'prop_not'@.
+prop_BooleanAlgebra
+  :: (BooleanAlgebra a, Ord a, Eq a, Show a)
+  => a -> a -> a -> CounterExample (BooleanAlgebraLawViolation a)
+prop_BooleanAlgebra a b c =
+     (fmapCounterExample BALVHeytingAlgebraLawViolation $ prop_HeytingAlgebra a b c)
+  /\ annotate (BALVNotLawViolation a) (prop_not a)
diff --git a/src/Algebra/Heyting.hs b/src/Algebra/Heyting.hs
--- a/src/Algebra/Heyting.hs
+++ b/src/Algebra/Heyting.hs
@@ -1,80 +1,96 @@
 {-# LANGUAGE CPP #-}
+
 module Algebra.Heyting
-  ( HeytingAlgebra (..)
+  ( -- * Heyting algebras
+    HeytingAlgebra (..)
+  , implies
+  , (<=>)
   , iff
   , iff'
   , toBoolean
-
-    -- * Properties
-    --
-    -- $properties
-  , prop_BoundedMeetSemiLattice
-  , prop_BoundedJoinSemiLattice
-  , prop_HeytingAlgebra
-  , prop_implies
+    -- * Boolean algebras
+  , BooleanAlgebra
   )
   where
 
-import Prelude hiding (not)
+
+import           Prelude hiding (not)
 import qualified Prelude
 
-import Control.Applicative    (Const (..))
-import Data.Functor.Identity  (Identity (..))
-import Data.Hashable          (Hashable)
-import Data.Proxy             (Proxy (..))
-import Data.Semigroup         (All (..), Any (..), Endo (..))
-import Data.Tagged            (Tagged (..))
-import Data.Universe.Class    (Finite, universe)
+import           Control.Applicative    (Const (..))
+import           Data.Functor.Identity  (Identity (..))
+import           Data.Hashable          (Hashable)
+import           Data.Proxy             (Proxy (..))
+import           Data.Semigroup         ( All (..)
+                                        , Any (..)
+                                        , Endo (..)
+                                        )
+import           Data.Tagged            (Tagged (..))
+import           Data.Universe.Class    (Finite, universe)
 import qualified Data.Map as M
-#if __GLASGOW_HASKELL__ >= 822
-import qualified Data.Map.Merge.Lazy as Merge
-#endif
-import qualified Data.Set as S
+import           Data.Set (Set)
+import qualified Data.Set as Set
 import qualified Data.HashMap.Lazy as HM
 import qualified Data.HashSet      as HS
 
-import Algebra.Lattice ( BoundedJoinSemiLattice (..)
-                       , BoundedMeetSemiLattice (..)
-                       , BoundedLattice
-                       , Meet (..)
-                       , Join (..)
-                       , (/\)
-                       , (\/)
-                       )
-import Algebra.Lattice.Dropped (Dropped (..))
-import Algebra.Lattice.Lifted (Lifted (..))
-import Algebra.Lattice.Levitated (Levitated)
+import           Algebra.Lattice ( BoundedJoinSemiLattice (..)
+                                 , BoundedMeetSemiLattice (..)
+                                 , BoundedLattice
+                                 , Meet (..)
+                                 , bottom
+                                 , top
+                                 , (/\)
+                                 , (\/)
+                                 )
+import           Algebra.Lattice.Dropped    (Dropped (..))
+import           Algebra.Lattice.Lifted     (Lifted (..))
+import           Algebra.Lattice.Levitated  (Levitated)
 import qualified Algebra.Lattice.Levitated as L
-import Algebra.Lattice.Ordered (Ordered (..))
-import Algebra.PartialOrd (leq)
-#ifdef EXPORT_PROPERTIES
-import Test.QuickCheck hiding (Ordered, (==>))
-import qualified Test.QuickCheck as QC
-#endif
+import           Algebra.Lattice.Ordered    (Ordered (..))
+import           Algebra.Lattice.Op         (Op (..))
+import           Algebra.PartialOrd         (leq)
 
+-- 
+-- Heyting algebras
+--
+
 -- |
 -- Heyting algebra is a bounded semi-lattice with implication which should
 -- satisfy the following law:
 --
 -- prop> x ∧ a ≤ b ⇔ x ≤ (a ⇒ b)
 --
+-- We also require that a Heyting algebra is a distributive lattice, which
+-- means any of the two equivalent conditions holds:
+--
+-- prop> a ∧ (b ∨ c) = a ∧ b ∨ a ∧ c
+-- prop> a ∨ (b ∧ c) = (a ∨ b) ∧ (a ∨ c)
+--
 -- This means @a ⇒ b@ is an
 -- [exponential object](https://ncatlab.org/nlab/show/exponential%2Bobject),
 -- which makes any Heyting algebra
 -- a [cartesian
 -- closed category](https://ncatlab.org/nlab/show/cartesian%2Bclosed%2Bcategory).
+-- This means that Curry isomorphism holds (which takes a form of an actual
+-- equality):
 --
--- Some useful properties of Heyting algebras:
+-- prop> a ⇒ (b ⇒ c) = (a ∧ b) ⇒ c
+--
+-- Some other useful properties of Heyting algebras:
 -- 
 -- prop> (a ⇒ b) ∧ a ≤ b
 -- prop> b ≤ a ⇒ a ∧ b
 -- prop> a ≤ b  iff a ⇒ b = ⊤
 -- prop> b ≤ b' then a ⇒ b ≤ a ⇒ b'
 -- prop> a'≤ a  then a' ⇒ b ≤ a ⇒ b
+-- prop> not (a ∧ b) = not (a ∨ b)
+-- prop> not (a ∨ b) = not a ∧ not b
 class BoundedLattice a => HeytingAlgebra a where
   -- |
   -- Default implementation: @a ==> b = not a \/ b@, it requires @not@ to
   -- satisfy Boolean axioms, which will make it into a Boolean algebra.
+  --
+  -- Fixity is less than fixity of both @'\/'@ and @'/\'@.
   (==>) :: a -> a -> a
   (==>) a b = not a \/ b
 
@@ -87,12 +103,20 @@
 
   {-# MINIMAL (==>) | not #-}
 
--- |
--- Less than fixity of both @'\/'@ and @'/\'@.
 infixr 4 ==>
 
+-- |
+-- @'implies'@ is an alias for @'==>'@
+implies :: HeytingAlgebra a => a -> a -> a
+implies = (==>)
+
+(<=>) :: HeytingAlgebra a => a -> a -> a
+a <=> b = (a ==> b) /\ (b ==> a)
+
+-- |
+-- @'iff'@ is an alias for @'<=>'@
 iff :: HeytingAlgebra a => a -> a -> a
-iff a b = (a ==> b) /\ (b ==> a)
+iff = (<=>)
 
 iff' :: (Eq a, HeytingAlgebra a) => a -> a -> Bool
 iff' a b = Meet top `leq` Meet (iff a b)
@@ -105,7 +129,7 @@
 toBoolean = not . not
 
 instance HeytingAlgebra Bool where
-  not     = Prelude.not
+  not = Prelude.not
 
 instance HeytingAlgebra All where
   All a ==> All b = All (a ==> b)
@@ -127,8 +151,10 @@
 instance HeytingAlgebra b => HeytingAlgebra (a -> b) where
   f ==> g = \a -> f a ==> g a
 
+#if MIN_VERSION_base(4,8,0)
 instance HeytingAlgebra a => HeytingAlgebra (Identity a) where
   (Identity a) ==> (Identity b) = Identity (a ==> b)
+#endif
 
 instance HeytingAlgebra a => HeytingAlgebra (Const a b) where
   (Const a) ==> (Const b) = Const (a ==> b)
@@ -143,6 +169,8 @@
 -- Dropped, Lifted, Levitated, Ordered
 --
 
+-- |
+-- Subdirectly irreducible Heyting algebra.
 instance (Eq a, HeytingAlgebra a) => HeytingAlgebra (Dropped a) where
   (Drop a) ==> (Drop b) | Meet a `leq` Meet b = Top
                         | otherwise           = Drop (a ==> b)
@@ -171,9 +199,9 @@
 --
 
 -- |
--- Power set: the cannoical example of a Boolean algebra
-instance (Ord a, Finite a) => HeytingAlgebra (S.Set a) where
-  not a = S.fromList universe `S.difference` a
+-- Power set: the canonical example of a Boolean algebra
+instance (Ord a, Finite a) => HeytingAlgebra (Set a) where
+  not a = Set.fromList universe `Set.difference` a
 
 instance (Eq a, Finite a, Hashable a) => HeytingAlgebra (HS.HashSet a) where
   not a = HS.fromList universe `HS.difference` a
@@ -184,14 +212,14 @@
   -- _xx__
   -- __yy_
   -- T_iTT where i = x ==> y; T = top; _ missing (or removed key)
-#if __GLASOW_HASKELL__ >= 822
+#if __GLASOW_HASKELL__ >= 804
   a ==> b =
     Merge.merge
       Merge.dropMissing                    -- drop if an element is missing in @b@
       (Merge.mapMissing (\_ _ -> top))     -- put @top@ if an element is missing in @a@
       (Merge.zipWithMatched (\_ -> (==>))) -- merge  matching elements with @==>@
       a b
-                            
+
     \/ M.fromList [(k, top) | k <- universe, not (M.member k a), not (M.member k b) ] 
                             -- for elements which are not in a, nor in b add
                             -- a @top@ key
@@ -207,56 +235,80 @@
     `HM.union` HM.map (const top) (HM.difference b a)
     `HM.union` HM.fromList [(k, top) | k <- universe, not (HM.member k a), not (HM.member k b)]
 
+-- 
+-- Boolean algebras
 --
--- $properties
+-- They are defined in the same module, to avoid module dependency: @Op a@ is
+-- a Boolean algebra (thus Heyting algebra in the first place), whenever @a@ is
+-- a Boolean algebra.
 --
--- /Properties are exported only if @export-properties@ cabal flag is defined./
-#ifdef EXPORT_PROPERTIES
 
 -- |
--- Verfifies bounded meet semilattice laws.
-prop_BoundedMeetSemiLattice :: (BoundedMeetSemiLattice a, Eq a, Show a)
-                            => a -> a -> a -> Property
-prop_BoundedMeetSemiLattice a b c =
-       counterexample "meet associativity" ((a /\ (b /\ c)) === ((a /\ b) /\ c))
-  .&&. counterexample "meet commutativity" ((a /\ b) === (b /\ a))
-  .&&. counterexample "meet idempotent" ((a /\ a) === a)
-  .&&. counterexample "meet identity" ((top /\ a) === a)
-  .&&. counterexample "meet order" (Meet (a /\ b) `leq` Meet a)
+-- Boolean algebra is a Heyting algebra which negation satisfies the law of
+-- excluded middle, i.e. either of the following:
+--
+-- prop> not . not == not
+--
+-- or
+--
+-- prop> x ∨ not x == top
+--
+-- Another characterisation of Boolean algebras is as
+-- [complemented](https://en.wikipedia.org/wiki/Complemented_lattice)
+-- [distributive lattices](https://ncatlab.org/nlab/show/distributive+lattice)
+-- where the complement satisfies the following three properties:
+--
+-- prop> (not a) ∧ a == bottom and (not a) ∨ a == top -- excluded middle law
+-- prop> not (not a) == a                             -- involution law
+-- prop> a ≤ b ⇒ not b ≤ not a                        -- order-reversing
+class HeytingAlgebra a => BooleanAlgebra a
 
--- |
--- Verfifies bounded join semilattice laws.
-prop_BoundedJoinSemiLattice :: (BoundedJoinSemiLattice a, Eq a, Show a) => a -> a -> a -> Property
-prop_BoundedJoinSemiLattice a b c =
-       counterexample "join associativity" ((a \/ (b \/ c)) === ((a \/ b) \/ c))
-  .&&. counterexample "join commutativity" ((a \/ b) === (b \/ a))
-  .&&. counterexample "join idempotent" ((a \/ a) === a)
-  .&&. counterexample "join identity" ((bottom \/ a) === a)
-  .&&. counterexample "join order" (Join a `leq` Join (a \/ b))
+--
+-- Instances
+--
 
--- |
--- Verifies the Heyting algebra law for @==>@:
--- for all @a@: @_ /\ a@ is left adjoint to  @a ==>@
-prop_implies :: (HeytingAlgebra a, Eq a, Show a)
-             => a -> a -> a -> Property
-prop_implies x a b =
-  counterexample ("Failed: x ≤ (a ⇒ b) then x ∧ a ≤ b\n\ta ⇒ b = " ++ show (a ==> b))
-    (Meet x `leq` Meet (a ==> b) QC.==> (Meet (x /\ a) `leq` Meet b))
-  .&&.
-  counterexample ("Failed: x ∧ a ≤ b then x ≤ (a ⇒ b)\n\ta ⇒ b = " ++ show (a ==> b))
-    (Meet (x /\ a) `leq` Meet b QC.==> (Meet x `leq` Meet (a ==> b)))
+instance BooleanAlgebra Bool
 
+instance BooleanAlgebra All
 
--- |
--- Usefull for testing valid instances of @'HeytingAlgebra'@ type class. It
--- validates:
---
--- * bounded lattice laws
--- * @'prop_implies'@
-prop_HeytingAlgebra :: (HeytingAlgebra a, Eq a, Show a)
-                    => a -> a -> a -> Property
-prop_HeytingAlgebra a b c = 
-       prop_BoundedJoinSemiLattice a b c
-  .&&. prop_BoundedMeetSemiLattice a b c
-  .&&. prop_implies a b c
+instance BooleanAlgebra Any
+
+instance BooleanAlgebra ()
+
+instance BooleanAlgebra (Proxy a)
+
+instance BooleanAlgebra a => BooleanAlgebra (Tagged t a)
+
+instance BooleanAlgebra b => BooleanAlgebra (a -> b)
+
+#if MIN_VERSION_base(4,8,0)
+instance BooleanAlgebra a => BooleanAlgebra (Identity a)
 #endif
+
+instance BooleanAlgebra a => BooleanAlgebra (Const a b)
+
+instance BooleanAlgebra a => BooleanAlgebra (Endo a)
+
+instance (BooleanAlgebra a, BooleanAlgebra b) => BooleanAlgebra (a, b)
+
+--
+-- containers
+--
+
+instance (Ord a, Finite a) => BooleanAlgebra (Set a)
+
+
+--
+-- Op
+--
+
+-- | Whenever @a@ is a Boolean Algebra, @Op a@ is a Boolean algebra as well,
+-- which in particular means that it is a Heyting algebra in the first place.
+--
+instance BooleanAlgebra a => HeytingAlgebra (Op a) where
+  (Op a) ==> (Op b) = Op (not a /\ b)
+
+-- | Every boolean algebra is isomorphic to power set @P(X)@ of some set @X@;
+-- then `not :: Op (P(X)) -> P(X)` is a lattice isomorphism, thus `Op (P(X))` is
+-- a boolean algebra, since @P(X)@ is.
+instance BooleanAlgebra a => BooleanAlgebra (Op a)
diff --git a/src/Algebra/Heyting/BoolRing.hs b/src/Algebra/Heyting/BoolRing.hs
new file mode 100644
--- /dev/null
+++ b/src/Algebra/Heyting/BoolRing.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE CPP #-}
+module Algebra.Heyting.BoolRing
+  ( BoolRing (..)
+  , Semiring (..)
+  , (<+>)
+  ) where
+
+import           Prelude hiding (not)
+
+import           Data.Monoid (Monoid (..))
+#if __GLASGOW_HASKELL__ < 804
+import           Data.Semigroup (Semigroup (..))
+#endif
+
+import           Algebra.Lattice (bottom, top, (/\), (\/))
+import           Data.Semiring (Semiring (..), (<+>))
+
+import           Algebra.Heyting
+
+-- |
+-- Newtype wraper which captures Boolean ring structure, which holds for every
+-- Heyting algebra.  A Boolean ring is a ring which satisfies:
+--
+-- prop> a <.> a = a
+--
+-- Some other properties:
+--
+-- prop> a <+> a = mempty                  -- thus it is a ring of characteristic 2
+-- prop> a <.> b = b <.> a                 -- hence it is a commutative ring
+-- prop> a <+> (b <+> c) = (a <+> b) <+> c -- multiplicative associativity
+newtype BoolRing a = BoolRing { getBoolRing :: a }
+
+-- | Sum is [symmetric differnce](https://en.wikipedia.org/wiki/Symmetric_difference).
+instance HeytingAlgebra a => Semigroup (BoolRing a) where
+  (BoolRing a) <> (BoolRing b) = BoolRing $ (not a /\ b) \/ (a /\ not b)
+
+-- | In a Boolean ring @a + a = 0@, hence @negate = id@.
+instance HeytingAlgebra a => Monoid (BoolRing a) where
+  mempty = BoolRing bottom
+
+#if __GLASGOW_HASKELL__ <= 804
+  mappend = (<>)
+#endif
+
+-- |  Multiplication is given by @'/\'@
+instance HeytingAlgebra a => Semiring (BoolRing a) where
+  BoolRing a <.> BoolRing b = BoolRing (a \/ b)
+  one = BoolRing top
diff --git a/src/Algebra/Heyting/CounterExample.hs b/src/Algebra/Heyting/CounterExample.hs
new file mode 100644
--- /dev/null
+++ b/src/Algebra/Heyting/CounterExample.hs
@@ -0,0 +1,102 @@
+{-# LANGUAGE BangPatterns #-}
+module Algebra.Heyting.CounterExample where
+
+import           Data.Set (Set)
+import qualified Data.Set as Set
+import           Text.Printf (printf)
+
+import           Algebra.Lattice (top)
+import           Algebra.Lattice.Lifted (Lifted)
+import qualified Algebra.Lattice.Lifted as Lifted
+import           Algebra.Lattice.Levitated (Levitated)
+import qualified Algebra.Lattice.Levitated as Levitated
+import           Algebra.Lattice.Op (Op (..))
+
+
+-- | A counter example type is a Heyting algebra; it useful for tests and
+-- properties.  It records all failures.  The truth value is represented by an
+-- empty set.  Since @'CounterExample' e@ is a Heyting algebra, it is useful in
+-- expressing properties that require assumptions.
+--
+type CounterExample a = Lifted (Op (Set a))
+
+fmapCounterExample
+  :: (Ord a, Ord b)
+  => (a -> b)
+  -> CounterExample a
+  -> CounterExample b
+fmapCounterExample = fmap . fmap . Set.map
+
+-- | A bijection from @e@ to atoms of the @'CounterExample' e@ lattice.
+--
+counterExample :: e -> CounterExample e
+counterExample e = Lifted.Lift (Op (Set.singleton e))
+
+-- | A lattice homomorphism from @'Bool'@ to @'CounterExample'@, which lifts
+-- @'False'@ to an atom of @'CounterExample'@ (uniquely determined by @e@) and
+-- which preserves the top element.
+--
+fromBool :: Ord e => e -> Bool -> CounterExample e
+fromBool e False = counterExample e
+fromBool _ True  = top
+
+-- |
+-- A homomorphism of /Heyting algebras/.
+--
+toBool :: CounterExample e -> Bool
+toBool (Lifted.Lift (Op s)) | Set.null s
+                            = True
+toBool _                    = False
+
+-- | Note that this map is not a lattice homomorphism (it does not preserve
+-- meet nor join).  It is also not a poset map in general.  Nevertheless, it preserves
+-- @top@ and @bottom@.
+--
+foldMapCounterExample
+  :: (Ord e, Monoid m)
+  => (e -> m)
+  -> CounterExample e
+  -> Levitated m
+foldMapCounterExample f (Lifted.Lift (Op s)) | Set.null s = Levitated.Top
+                                             | otherwise  = Levitated.Levitate (foldMap f s)
+foldMapCounterExample _ Lifted.Bottom        = Levitated.Bottom
+
+-- |
+-- Map a CounterExample to @'Levitated' String@.  Each set of counter example
+-- is concatenated into a single comma separated string.  This is useful for
+-- printing counter examples in tests.  See @'fromCounterExample''@.
+--
+fromCounterExample :: Show a => CounterExample a -> Levitated String 
+fromCounterExample Lifted.Bottom        = Levitated.Bottom
+fromCounterExample (Lifted.Lift (Op s)) | Set.null s
+                                        = Levitated.Top
+                                        | otherwise
+                                        = Levitated.Levitate (Set.foldl' go "" s)
+  where
+    go "" b = show b
+    go !a b = printf "%s, %s" a (show b)
+
+-- | Map `CounterExample` to a Maybe, representing @'Levitated.Top'@ as
+-- @'Nothing'@ and mapping both @'Levitated.Levitate'@ and @'Levitate.Bottom'@ to
+-- a @Just String@.
+--
+fromCounterExample' :: Show a => CounterExample a -> Maybe String
+fromCounterExample' ce = case fromCounterExample ce of
+  Levitated.Top        -> Nothing
+  Levitated.Levitate s -> Just s
+  Levitated.Bottom     -> Just ""
+
+-- | Add a counter example.  This is simply lifts the bottom to an atom given
+-- by @e@, otherwise it preserves the @'CounterExample' e@.
+--
+-- Note that take join of @\e es = counterExample e \\/ es@ will return @top@ if
+-- @e@ if e is not in the set @es@; thus this map is not defined with using @\\/@.
+--
+annotate :: Ord e => e -> CounterExample e -> CounterExample e
+annotate e Lifted.Bottom = counterExample e
+annotate _ es            = es
+
+(===) :: (Ord e, Eq a) => a -> a -> CounterExample e
+a === b = if a == b then Lifted.Lift (Op Set.empty) else Lifted.Bottom
+
+infixr 4 ===
diff --git a/src/Algebra/Heyting/Free.hs b/src/Algebra/Heyting/Free.hs
--- a/src/Algebra/Heyting/Free.hs
+++ b/src/Algebra/Heyting/Free.hs
@@ -1,29 +1,28 @@
 {-# LANGUAGE TypeFamilies #-}
 module Algebra.Heyting.Free
   ( FreeHeyting (..)
+  , atom
   ) where
 
-import Prelude hiding (not)
+import           Prelude hiding (not)
 
-import Control.Monad (ap)
-import Algebra.Lattice
-  ( BoundedJoinSemiLattice (..)
-  , JoinSemiLattice (..)
-  , BoundedMeetSemiLattice (..)
-  , MeetSemiLattice (..)
-  , BoundedLattice
-  , Lattice
-  )
-import Data.Algebra.Free
-  ( AlgebraType0
-  , AlgebraType
-  , FreeAlgebra (..)
-  , proof
-  , fmapFree
-  , bindFree
-  )
+import           Control.Monad     (ap)
+import           Algebra.Lattice   ( BoundedJoinSemiLattice (..)
+                                   , JoinSemiLattice (..)
+                                   , BoundedMeetSemiLattice (..)
+                                   , MeetSemiLattice (..)
+                                   , BoundedLattice
+                                   , Lattice
+                                   )
+import           Data.Algebra.Free ( AlgebraType0
+                                   , AlgebraType
+                                   , FreeAlgebra (..)
+                                   , proof
+                                   , fmapFree
+                                   , bindFree
+                                   )
 
-import Algebra.Heyting (HeytingAlgebra (..))
+import           Algebra.Heyting   (HeytingAlgebra (..))
 
 -- |
 -- Free Heyting algebra.  @'FreeAlgebra'@ instance provides all the usual
@@ -31,7 +30,8 @@
 --
 -- The
 -- [graph](https://en.wikipedia.org/wiki/Heyting_algebra#/media/File:Rieger-Nishimura.svg)
--- of free Heyting algebra with one generator, i.e. @'FreeHeyting' ()@.
+-- of free Heyting algebra with one generator\/atom, i.e. @'FreeHeyting' ()@.
+--
 newtype FreeHeyting a = FreeHeyting
   { runFreeHeyting :: forall h . HeytingAlgebra h => (a -> h) -> h }
 
@@ -55,10 +55,17 @@
   FreeHeyting f ==> FreeHeyting g = FreeHeyting (\inj -> f inj ==> g inj)
   not (FreeHeyting f)             = FreeHeyting (\inj -> not (f inj))
 
+-- |
+-- Construct an atom of the @'FreeHeyting'@ lattice (in the laguage of free
+-- algebra, it is called a generator, e.g. @atom = returnFree@).
+--
+atom :: a -> FreeHeyting a
+atom = \a -> FreeHeyting $ \inj -> inj a
+
 type instance AlgebraType0 FreeHeyting a = ()
 type instance AlgebraType  FreeHeyting a = HeytingAlgebra a
 instance FreeAlgebra FreeHeyting where
-  returnFree a = FreeHeyting (\inj -> inj a)
+  returnFree = atom
   foldMapFree f (FreeHeyting inj) = inj f
 
   codom  = proof
diff --git a/src/Algebra/Heyting/Layered.hs b/src/Algebra/Heyting/Layered.hs
--- a/src/Algebra/Heyting/Layered.hs
+++ b/src/Algebra/Heyting/Layered.hs
@@ -3,22 +3,21 @@
   , layer
   ) where
 
-import Prelude
+import           Prelude
 
-import Algebra.Lattice
-  ( BoundedJoinSemiLattice (..)
-  , JoinSemiLattice (..)
-  , BoundedMeetSemiLattice (..)
-  , MeetSemiLattice (..)
-  , BoundedLattice
-  , Lattice
-  )
+import           Algebra.Lattice ( BoundedJoinSemiLattice (..)
+                                 , JoinSemiLattice (..)
+                                 , BoundedMeetSemiLattice (..)
+                                 , MeetSemiLattice (..)
+                                 , BoundedLattice
+                                 , Lattice
+                                 )
 
-import Algebra.Heyting (HeytingAlgebra (..))
+import           Algebra.Heyting (HeytingAlgebra (..))
 
 -- |
 -- Layer one Heyting algebra on top of the other.  Note: this is not
--- a categorial sum.
+-- a categorical sum.
 data Layered a b
   = Lower a
   | Upper b
diff --git a/src/Algebra/Heyting/Properties.hs b/src/Algebra/Heyting/Properties.hs
new file mode 100644
--- /dev/null
+++ b/src/Algebra/Heyting/Properties.hs
@@ -0,0 +1,189 @@
+{-# LANGUAGE TupleSections #-}
+-- |
+-- Properties of Heyting algebras; useful for testing lawfulness of instances.
+--
+module Algebra.Heyting.Properties where
+
+import           Prelude hiding (not)
+
+import           Data.List (intersperse)
+import           Data.Semigroup ((<>))
+
+import           Algebra.Lattice ( Lattice
+                                 , BoundedJoinSemiLattice
+                                 , BoundedMeetSemiLattice
+                                 , Meet (..)
+                                 , Join (..)
+                                 , top
+                                 , bottom
+                                 , (/\)
+                                 , (\/)
+                                 )
+import           Algebra.PartialOrd (leq)
+import           Algebra.Heyting
+import           Algebra.Heyting.CounterExample
+
+data BoundedMeetSemiLatticeLawViolation a
+  = BMSLVNonAssociative a a a
+  | BMSLVNonCommutative a a
+  | BMSLVNonIdempotent a
+  | BMSLVNonUnital a
+  | BMSLVMeetOrderViolation a a
+  deriving (Eq, Ord)
+
+instance Show a => Show (BoundedMeetSemiLatticeLawViolation a) where
+  show (BMSLVNonAssociative a b c) = withArgs "a ∧ (b ∧ c) ≠ (a ∧ b) ∧ c" [a, b, c]
+  show (BMSLVNonCommutative a b) = withArgs "a ∧ b ٍ≠ b ∧ a" [a, b]
+  show (BMSLVNonIdempotent a) = withArgs "a ∧ a ≠ a" [a]
+  show (BMSLVNonUnital a) = withArgs "a ∧ top ≠ a" [a]
+  show (BMSLVMeetOrderViolation a b) = withArgs "a ∧ b > a" [a, b]
+
+withArgs :: Show a => String -> [a] -> String
+withArgs s bs = s ++ "\n\t" ++ foldr (<>) mempty (intersperse "\n\t" (map show bs))
+
+-- |
+-- Verifies bounded meet semilattice laws.
+prop_BoundedMeetSemiLattice
+  :: (BoundedMeetSemiLattice a, Ord a, Eq a, Show a)
+  => a -> a -> a -> CounterExample (BoundedMeetSemiLatticeLawViolation a)
+prop_BoundedMeetSemiLattice a b c =
+  annotate (BMSLVNonAssociative a b c) ((a /\ (b /\ c)) === ((a /\ b) /\ c))
+  /\ annotate (BMSLVNonCommutative a b) ((a /\ b) === (b /\ a))
+  /\ annotate (BMSLVNonIdempotent a) ((a /\ a) === a)
+  /\ annotate (BMSLVNonUnital a) ((top /\ a) === a)
+  /\ annotate (BMSLVMeetOrderViolation a b) ((Meet (a /\ b) `leq` Meet a) === True)
+
+data BoundedJoinSemiLatticeLawViolation a
+  = BJSLVNonAssociative a a a
+  | BJSLVNonCommutative a a
+  | BJSLVNonIdempotent a
+  | BJSLVNonUnital a
+  | BJSLVJoinOrderViolation a a
+  deriving (Eq, Ord)
+
+instance Show a => Show (BoundedJoinSemiLatticeLawViolation a) where
+  show (BJSLVNonAssociative a b c) = withArgs "a ∨ (b ∨ c) ≠ (a ∨ b) ∨ c" [a, b, c]
+  show (BJSLVNonCommutative a b) = withArgs "a ∨ b ٍ≠ b ∨ a" [a, b]
+  show (BJSLVNonIdempotent a) = withArgs "a ∨ a ≠ a" [a]
+  show (BJSLVNonUnital a) = withArgs "a ∨ top ≠ a" [a]
+  show (BJSLVJoinOrderViolation a b) = withArgs "a ∨ b > a" [a, b]
+
+-- |
+-- Verifies bounded join semilattice laws.
+prop_BoundedJoinSemiLattice
+  :: (BoundedJoinSemiLattice a, Ord a, Eq a, Show a)
+  => a -> a -> a -> CounterExample (BoundedJoinSemiLatticeLawViolation a)
+prop_BoundedJoinSemiLattice a b c =
+     annotate (BJSLVNonAssociative a b c)
+        ((a \/ (b \/ c)) === ((a \/ b) \/ c))
+  /\ annotate (BJSLVNonCommutative a b)
+        ((a \/ b) === (b \/ a))
+  /\ annotate (BJSLVNonIdempotent a)
+        ((a \/ a) === a)
+  /\ annotate (BJSLVNonUnital a)
+        ((bottom \/ a) === a)
+  /\ fromBool (BJSLVJoinOrderViolation a b)
+        (Join a `leq` Join (a \/ b))
+
+data DistributiveLatticeLawViolation a
+  = DLLVJoinOverMeetViolation a a a
+  | DLLVMeetOverJoinViolation a a a
+  deriving (Eq, Ord)
+
+instance Show a => Show (DistributiveLatticeLawViolation a) where
+  show (DLLVJoinOverMeetViolation a b c) = withArgs "a ∧ (b ∨ c) ≠ a ∧ b ∨ a ∧ c" [a, b, c]
+  show (DLLVMeetOverJoinViolation a b c) = withArgs "a ∨ (b ∧ c) ≠ (a ∨ b) ∧ (a ∨ c)" [a, b, c]
+
+-- |
+-- Distributivity laws for a lattice.
+prop_DistributiveLattice
+  :: (Lattice a, Ord a, Eq a, Show a)
+  => a -> a -> a -> CounterExample (DistributiveLatticeLawViolation a)
+prop_DistributiveLattice a b c =
+     annotate (DLLVJoinOverMeetViolation a b c)
+      ((a /\ (b \/ c)) === ((a /\ b) \/ (a /\ c)))
+  /\ annotate (DLLVMeetOverJoinViolation a b c)
+      ((a \/ (b /\ c)) === ((a \/ b) /\ (a \/ c)))
+
+data HeytingAlgebraLawViolation a
+  = HAVImplication1 a a a
+  | HAVImplication2 a a a
+  | HAVNot a a
+  | HAVNotAndMeet a a
+  | HAVNotAndJoin a a
+  | HAVImplicationAndOrd a a
+  | HAVDistributiveLatticeLawViolation (DistributiveLatticeLawViolation a)
+  | HAVBoundedJoinSemilatticeLawViolation (BoundedJoinSemiLatticeLawViolation a)
+  | HAVBoundedMeetSemilatticeLawViolation (BoundedMeetSemiLatticeLawViolation a)
+  deriving (Eq, Ord)
+
+instance Show a => Show (HeytingAlgebraLawViolation a) where
+  show (HAVImplication1 x a b)=
+    withArgs "x ≤ (a ⇒ b) then x ∧ a ≤ b" [x, a, b]
+
+  show (HAVImplication2 x a b) =
+      withArgs "x ∧ a ≤ b then x ≤ (a ⇒ b)" [x, a, b]
+
+  show (HAVNot a b) =
+    withArgs "a ≤ b ⇏ not a" [a, b]
+
+  show (HAVNotAndMeet a b) =
+    withArgs "not (a ∧ b) ≠ not a ∨ not b" [a, b]
+
+  show (HAVNotAndJoin a b) =
+    withArgs "not (a ∨ b) ≠ not a ∧ not b" [a, b]
+
+  show (HAVImplicationAndOrd a b) =
+    withArgs "(a ⇒ b) ∧ a ≰ b" [a, b]
+
+  show (HAVDistributiveLatticeLawViolation e)    = show e
+  show (HAVBoundedJoinSemilatticeLawViolation e) = show e
+  show (HAVBoundedMeetSemilatticeLawViolation e) = show e
+
+-- |
+-- Verifies the Heyting algebra law for @==>@:
+-- for all @a@: @_ /\ a@ is left adjoint to  @a ==>@
+-- and some other properties that are a consequence of that.
+prop_implies :: (HeytingAlgebra a, Ord a, Eq a, Show a)
+             => a -> a -> a -> CounterExample (HeytingAlgebraLawViolation a)
+prop_implies x a b =
+    fromBool
+      (HAVImplication1 x a b)
+      (Meet x `leq` Meet (a ==> b) ==> (Meet (x /\ a) `leq` Meet b))
+  /\ fromBool
+      (HAVImplication2 x a b)
+      (Meet (x /\ a) `leq` Meet b ==> (Meet x `leq` Meet (a ==> b)))
+  /\ fromBool
+      (HAVNot a b)
+      (Meet a `leq` Meet b ==> (Meet (not b) `leq` Meet (not a)))
+  /\ annotate
+      (HAVNotAndMeet a b)
+      (not (a /\ b) === (not a \/ not b))
+  /\ annotate
+      (HAVNotAndJoin a b)
+      (not (a \/ b) === (not a /\ not b))
+  /\ fromBool
+      (HAVImplicationAndOrd a a)
+      (Meet ((a ==> b) /\ a) `leq` Meet b)
+
+-- |
+-- Useful for testing valid instances of @'HeytingAlgebra'@ type class. It
+-- validates:
+--
+-- * bounded lattice laws
+-- * @'prop_implies'@
+prop_HeytingAlgebra
+  :: (HeytingAlgebra a, Ord a, Eq a, Show a)
+  => a -> a -> a -> CounterExample (HeytingAlgebraLawViolation a)
+prop_HeytingAlgebra a b c =
+
+     fmapCounterExample HAVBoundedJoinSemilatticeLawViolation
+        (prop_BoundedJoinSemiLattice a b c)
+
+  /\ fmapCounterExample HAVBoundedMeetSemilatticeLawViolation
+        (prop_BoundedMeetSemiLattice a b c)
+
+  /\ fmapCounterExample HAVDistributiveLatticeLawViolation
+        (prop_DistributiveLattice a b c)
+
+  /\ prop_implies a b c
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,31 +1,208 @@
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+
 module Main where
 
-import Algebra.Lattice ( JoinSemiLattice
-                       , BoundedJoinSemiLattice
-                       , MeetSemiLattice
-                       , BoundedMeetSemiLattice
-                       , Lattice
-                       , BoundedLattice
-                       )
-import Algebra.Lattice.Dropped (Dropped (..))
-import Algebra.Lattice.Lifted (Lifted (..))
-import Algebra.Lattice.Levitated (Levitated)
+import           Data.Universe.Class (Universe (..), Finite)
+import           Data.Set (Set)
+import qualified Data.Set as Set
+import           Data.Map (Map)
+import qualified Data.Map as Map
+
+import           Algebra.Lattice ( JoinSemiLattice
+                                 , BoundedJoinSemiLattice
+                                 , MeetSemiLattice
+                                 , BoundedMeetSemiLattice
+                                 , Lattice
+                                 , BoundedLattice
+                                 )
+import           Algebra.Lattice.Dropped (Dropped (..))
+import           Algebra.Lattice.Lifted (Lifted (..))
+import           Algebra.Lattice.Levitated (Levitated)
 import qualified Algebra.Lattice.Levitated as L
-import Algebra.Lattice.Ordered (Ordered (..))
-import Data.Universe.Class (Universe (..), Finite)
-import qualified Data.Set as S
-import qualified Data.Map as M
+import           Algebra.Lattice.Op (Op (..))
+import           Algebra.Lattice.Ordered (Ordered (..))
 
-import Algebra.Boolean
-import Algebra.Heyting
-import Algebra.Heyting.Layered
+import           Algebra.Boolean
+import           Algebra.Boolean.Properties
+import           Algebra.Heyting
+import           Algebra.Heyting.CounterExample
+import           Algebra.Heyting.Properties
+import           Algebra.Heyting.Layered
 
-import Test.Tasty
-import Test.Tasty.QuickCheck hiding (Ordered)
+import           Test.Tasty
+import           Test.Tasty.QuickCheck hiding (Ordered)
 
--- | Arbitrary wrapper
+main :: IO ()
+main = defaultMain tests
+
+counterExampleProperty
+  :: Show e
+  => CounterExample e
+  -> Property
+counterExampleProperty = maybe (property True) (flip counterexample False) . fromCounterExample'
+
+-- 
+-- List of tast cases
+--
+
+tests :: TestTree
+tests =
+  testGroup "heyting-algebras tests"
+    [ testGroup "Boolean algebras"
+        [ testProperty "Bool"                                         prop_boolean_Bool
+        , testProperty "(Bool, Bool)"                                 prop_boolean_BoolBool
+        , testProperty "Boolean (Lifted Bool)"                        prop_boolean_LiftedBool
+        , testProperty "Boolean (Dropped Bool)"                       prop_boolean_DroppedBool
+        , testProperty "(Set S5)"                                     prop_boolean_Set
+        ]
+    , testGroup "Non Boolean algebras"
+        [ testProperty "Not a BooleanAlgebra (Lifted Bool)"           prop_non_boolean_LiftedBool
+        , testProperty "Not a BooleanAlgebra (Dropped Bool)"          prop_non_boolean_DroppedBool
+        , testProperty "Not a BooleanAlgebra Levitated (Ordered Int)" prop_non_boolean_LevitatedOrderedInt
+        ]
+    , testGroup "Heyting algebras"
+        [ testProperty "Lifted Bool"                                  prop_heyting_LiftedBool
+        , testProperty "Dropped Bool"                                 prop_heyting_DroppedBool
+        , testProperty "Layered Bool Bool"                            prop_heyting_LayeredBoolBool
+        , testProperty "Levitated Bool"                               prop_heyting_LevitatedBool
+        , testProperty "Sum (Lifted Bool) (Dropped Bool)"             prop_heyting_LayeredLiftedDropped
+        , testProperty "Levitated (Ordered Int)"                      prop_heyting_LevitatedOrderedInt
+        , testProperty "Map S5 Bool"                                  prop_heyting_MapS5Bool
+        , testProperty "Dropped (Lifted Bool)"                        prop_heyting_DroppedLiftedBool
+        , testProperty "Lifted (Dropped Bool)"                        prop_heyting_LiftedDroppedBool
+        , testProperty "Lifted (Lifted Bool)"                         prop_heyting_LiftedLiftedBool
+        , testProperty "Dropped (Dropped Bool)"                       prop_heyting_DroppedDroppedBool
+        , testProperty "CounterExample"                               prop_heyting_CounterExample
+        ]
+    ]
+
+--
+-- Boolean algebra tests
+--
+
+type BooleanProp a = a -> a -> a -> Property
+
+prop_boolean_Bool
+  :: BooleanProp Bool
+prop_boolean_Bool =
+  (fmap . fmap) counterExampleProperty . prop_BooleanAlgebra
+
+prop_boolean_BoolBool
+  :: BooleanProp (Bool, Bool)
+prop_boolean_BoolBool =
+  (fmap . fmap) counterExampleProperty . prop_BooleanAlgebra
+
+prop_boolean_LiftedBool
+  :: BooleanProp (Arb (Boolean (Arb (Lifted Bool))))
+prop_boolean_LiftedBool =
+  (fmap . fmap) counterExampleProperty . prop_BooleanAlgebra
+
+prop_boolean_DroppedBool
+  :: BooleanProp (Arb (Boolean (Arb (Dropped Bool))))
+prop_boolean_DroppedBool =
+  (fmap . fmap) counterExampleProperty . prop_BooleanAlgebra
+
+prop_boolean_Set
+  :: BooleanProp (Arb (Set S5))
+prop_boolean_Set =
+  (fmap . fmap) counterExampleProperty . prop_BooleanAlgebra
+
+--
+-- Non Boolean algebra tests
+--
+
+type NonBooleanProp a = a -> Property
+
+prop_non_boolean_LiftedBool
+  :: NonBooleanProp (Arb (Lifted Bool))
+prop_non_boolean_LiftedBool =
+    expectFailure
+  . counterExampleProperty @String
+  . prop_not
+
+prop_non_boolean_DroppedBool
+  :: NonBooleanProp (Arb (Dropped Bool))
+prop_non_boolean_DroppedBool =
+    expectFailure
+  . counterExampleProperty @String
+  . prop_not
+
+prop_non_boolean_LevitatedOrderedInt
+  :: NonBooleanProp (Arb (Levitated (Arb (Ordered Int))))
+prop_non_boolean_LevitatedOrderedInt =
+    expectFailure
+  . counterExampleProperty @String
+  . prop_not
+
+--
+-- Heyting algebra tests
+--
+
+type HeytingProp a = a -> a -> a -> Property
+
+prop_heyting_LiftedBool
+  :: HeytingProp (Arb (Lifted Bool))
+prop_heyting_LiftedBool =
+  (fmap . fmap) counterExampleProperty . prop_HeytingAlgebra
+
+prop_heyting_DroppedBool
+  :: HeytingProp (Arb (Dropped Bool))
+prop_heyting_DroppedBool =
+  (fmap . fmap) counterExampleProperty . prop_HeytingAlgebra
+
+prop_heyting_LayeredBoolBool
+  :: HeytingProp (Arb (Layered Bool Bool))
+prop_heyting_LayeredBoolBool =
+  (fmap . fmap) counterExampleProperty . prop_HeytingAlgebra
+
+prop_heyting_LevitatedBool
+  :: HeytingProp (Arb (Levitated Bool))
+prop_heyting_LevitatedBool =
+  (fmap . fmap) counterExampleProperty . prop_HeytingAlgebra
+
+prop_heyting_LayeredLiftedDropped
+  :: HeytingProp (Arb (Layered (Arb (Lifted Bool)) (Arb (Dropped Bool))))
+prop_heyting_LayeredLiftedDropped =
+  (fmap . fmap) counterExampleProperty . prop_HeytingAlgebra
+
+prop_heyting_LevitatedOrderedInt
+  :: HeytingProp (Arb (Levitated (Arb (Ordered Int))))
+prop_heyting_LevitatedOrderedInt =
+  (fmap . fmap) counterExampleProperty . prop_HeytingAlgebra
+
+prop_heyting_MapS5Bool
+  :: HeytingProp (Arb (Map S5 Bool))
+prop_heyting_MapS5Bool =
+  (fmap . fmap) counterExampleProperty . prop_HeytingAlgebra
+
+prop_heyting_DroppedLiftedBool
+  :: HeytingProp (Composed Dropped Lifted Bool)
+prop_heyting_DroppedLiftedBool =
+  (fmap . fmap) counterExampleProperty . prop_HeytingAlgebra
+
+prop_heyting_LiftedDroppedBool
+  :: HeytingProp (Composed Lifted Dropped Bool)
+prop_heyting_LiftedDroppedBool =
+  (fmap . fmap) counterExampleProperty . prop_HeytingAlgebra
+
+prop_heyting_LiftedLiftedBool
+  :: HeytingProp (Composed Lifted Lifted Bool)
+prop_heyting_LiftedLiftedBool =
+  (fmap . fmap) counterExampleProperty . prop_HeytingAlgebra
+
+prop_heyting_DroppedDroppedBool
+  :: HeytingProp (Composed Dropped Dropped Bool)
+prop_heyting_DroppedDroppedBool =
+  (fmap . fmap) counterExampleProperty . prop_HeytingAlgebra
+
+prop_heyting_CounterExample
+  :: HeytingProp (Composed Lifted Op (Set S5))
+prop_heyting_CounterExample =
+  (fmap . fmap) counterExampleProperty . prop_HeytingAlgebra
+
+-- | Arbitrary wrapper for varous lattices.
+--
 newtype Arb a = Arb a
   deriving ( JoinSemiLattice
            , BoundedJoinSemiLattice
@@ -42,24 +219,24 @@
 instance Show a => Show (Arb a) where
   show (Arb a) = show a
 
-instance (Finite k, Arbitrary k, Arbitrary v, Ord k) => Arbitrary (Arb (M.Map k v)) where
+instance (Finite k, Arbitrary k, Arbitrary v, Ord k) => Arbitrary (Arb (Map k v)) where
   arbitrary = frequency 
-    [ (1, Arb . M.fromList <$> arbitrary)
-    , (4, Arb . M.fromList . zip universe <$> vectorOf (length (universe @k)) arbitrary)
-    , (6, return $ Arb M.empty)
+    [ (1, return $ Arb Map.empty)
+    , (1, Arb . Map.fromList . zip universe <$> vectorOf (length (universe @k)) arbitrary)
+    , (8, Arb . Map.fromList <$> arbitrary)
     ]
 
 instance Arbitrary a => Arbitrary (Arb (Lifted a)) where
   arbitrary = Arb . maybe Bottom Lift <$> arbitrary
   shrink (Arb Bottom)   = []
   shrink (Arb (Lift a)) =
-    Arb Bottom : [ Arb (Lift a') | a' <- shrink a ]
+    Arb Bottom : (Arb . Lift <$> shrink a)
 
 instance Arbitrary a => Arbitrary (Arb (Dropped a)) where
   arbitrary = Arb . maybe Top Drop <$> arbitrary
   shrink (Arb Top)   = []
   shrink (Arb (Drop a)) =
-    Arb Top : [ Arb (Drop a') | a' <- shrink a ]
+    Arb Top : (Arb . Drop <$> shrink a)
 
 instance Arbitrary a => Arbitrary (Arb (Levitated a)) where
   arbitrary = frequency
@@ -75,9 +252,14 @@
     : [ Arb (L.Levitate a') | a' <- shrink a ]
   shrink (Arb L.Top) = []
 
+instance (Arbitrary a, HeytingAlgebra a, Eq a) => Arbitrary (Arb (Boolean a)) where
+  arbitrary = Arb . boolean <$> arbitrary
+  shrink (Arb a) = filter (/= Arb a) (Arb . boolean <$> shrink (runBoolean a))
+
+ 
 instance Arbitrary a => Arbitrary (Arb (Ordered a)) where
   arbitrary = Arb . Ordered <$> arbitrary
-  shrink (Arb (Ordered a)) = [ Arb (Ordered a') | a' <- shrink a ]
+  shrink (Arb (Ordered a)) = Arb . Ordered <$> shrink a
 
 data S5 = S1 | S2 | S3 | S4 | S5
   deriving (Ord, Eq, Show)
@@ -90,20 +272,20 @@
 instance Arbitrary S5 where
   arbitrary = elements universe
 
-instance (Arbitrary a, Ord a) => Arbitrary (Arb (S.Set a)) where
-  arbitrary = Arb . S.fromList <$> arbitrary
-  shrink (Arb as) = [ Arb (S.fromList as') | as' <- shrink (S.toList as) ]
+instance (Arbitrary a, Ord a) => Arbitrary (Arb (Set a)) where
+  arbitrary       = Arb . Set.fromList <$> arbitrary
+  shrink (Arb as) = [ Arb (Set.fromList as') | as' <- shrink (Set.toList as) ]
 
 instance (Arbitrary a, Arbitrary b) => Arbitrary (Arb (Layered a b)) where
   arbitrary = oneof
     [ Arb . Lower <$> arbitrary
     , Arb . Upper <$> arbitrary
     ]
-  shrink (Arb (Lower a)) = [ Arb (Lower a') | a' <- shrink a ]
-  shrink (Arb (Upper b)) = [ Arb (Upper b') | b' <- shrink b ]
+  shrink (Arb (Lower a)) = Arb . Lower <$> shrink a
+  shrink (Arb (Upper b)) = Arb . Upper <$> shrink b
 
--- Another arbitrary newtype wrapper; using tagged type let us avoid
--- overlapping instances.
+-- | Arbitrary newtype wrapper for compositions of heigher kinded types.
+--
 newtype Composed f g a = Composed (f (g a))
   deriving ( JoinSemiLattice
            , BoundedJoinSemiLattice
@@ -124,40 +306,40 @@
   arbitrary = frequency
     [ (1, return $ Composed Top)
     , (1, return $ Composed (Drop Bottom))
-    , (2, Composed . Drop . Lift  <$> arbitrary)
+    , (8, Composed . Drop . Lift  <$> arbitrary)
     ]
 
   shrink (Composed Top)             = []
   shrink (Composed (Drop Bottom))   = [Composed Top]
   shrink (Composed (Drop (Lift a))) =
-       [ Composed Top, Composed (Drop Bottom) ]
+       [ Composed (Drop Bottom) ]
     ++ [ Composed (Drop (Lift a')) | a' <- shrink a ]
 
 instance Arbitrary a => Arbitrary (Composed Lifted Dropped a) where
   arbitrary = frequency
     [ (1, return $ Composed Bottom)
     , (1, return $ Composed (Lift Top))
-    , (2, Composed . Lift . Drop  <$> arbitrary)
+    , (8, Composed . Lift . Drop  <$> arbitrary)
     ]
 
 instance Arbitrary a => Arbitrary (Composed Lifted Lifted a) where
   arbitrary = frequency
     [ (1, return $ Composed Bottom)
     , (1, return $ Composed (Lift Bottom))
-    , (2, Composed . Lift . Lift  <$> arbitrary)
+    , (8, Composed . Lift . Lift  <$> arbitrary)
     ]
 
   shrink (Composed Bottom)          = []
   shrink (Composed (Lift Bottom))   = [Composed Bottom]
   shrink (Composed (Lift (Lift a))) =
-       [ Composed Bottom, Composed (Lift Bottom) ]
+       [ Composed (Lift Bottom) ]
     ++ [ Composed (Lift (Lift a')) | a' <- shrink a ]
 
 instance Arbitrary a => Arbitrary (Composed Dropped Dropped a) where
   arbitrary = frequency
     [ (1, return $ Composed Top)
     , (1, return $ Composed (Drop Top))
-    , (2, Composed . Drop . Drop  <$> arbitrary)
+    , (8, Composed . Drop . Drop  <$> arbitrary)
     ]
 
   shrink (Composed Top)          = []
@@ -166,36 +348,8 @@
        [ Composed Top, Composed (Drop Top) ]
     ++ [ Composed (Drop (Drop a')) | a' <- shrink a ]
 
-
-main :: IO ()
-main = defaultMain tests
-
-tests :: TestTree
-tests =
-  testGroup "heyting-algebras tests"
-    [ testGroup "Boolean algebras"
-        [ testProperty "Bool"                  $ prop_BooleanAlgebra @Bool
-        , testProperty "(Bool, Bool)"          $ prop_BooleanAlgebra @(Bool, Bool)
-        , testProperty "Boolean (Lifted Bool)" $ prop_BooleanAlgebra @(Boolean (Arb (Lifted Bool)))
-        , testProperty "(Set S5)"              $ prop_BooleanAlgebra @(Arb (S.Set S5))
-        ]
-    , testGroup "Non Boolean algebras"
-        [ testProperty "Not a BooleanAlgebra (Lifted Bool)"    $ expectFailure $ prop_not @(Arb (Lifted Bool))
-        , testProperty "Not a BooleanAlgebra (Dropped Bool)"   $ expectFailure $ prop_not @(Arb (Dropped Bool))
-        , testProperty "Not a BooleanAlgebra Levitated (Ordered Int)" $ expectFailure $ prop_BooleanAlgebra @(Arb (Levitated (Arb (Ordered Int))))
-        ]
-    , testGroup "Heyting algebras"
-        [ testProperty "Lifted Bool"            $ prop_HeytingAlgebra @(Arb (Lifted Bool))
-        , testProperty "Dropped Bool"           $ prop_HeytingAlgebra @(Arb (Dropped Bool))
-        , testProperty "Layered Bool Bool"      $ prop_HeytingAlgebra @(Arb (Layered Bool Bool))
-        , testProperty "Levitated Bool"         $ prop_HeytingAlgebra @(Arb (Levitated Bool))
-        , testProperty "Sum (Lifted Bool) (Dropped Bool)"
-                                                $ prop_HeytingAlgebra @(Arb (Layered (Arb (Lifted Bool)) (Arb (Dropped Bool))))
-        , testProperty "Levitated (Ordered Int)" $ prop_HeytingAlgebra @(Arb (Levitated (Arb (Ordered Int))))
-        , testProperty "Map S5 Bool"            $ prop_HeytingAlgebra @(Arb (M.Map S5 Bool))
-        , testProperty "Dropped (Lifted Bool)"  $ prop_HeytingAlgebra @(Composed Dropped Lifted Bool)
-        , testProperty "Lifted (Dropped Bool)"  $ prop_HeytingAlgebra @(Composed Lifted Dropped Bool)
-        , testProperty "Lifted (Lifted Bool)"   $ prop_HeytingAlgebra @(Composed Lifted Lifted Bool)
-        , testProperty "Dropped (Dropped Bool)" $ prop_HeytingAlgebra @(Composed Dropped Dropped Bool)
-        ]
+instance Arbitrary a => Arbitrary (Composed Lifted Op a) where
+  arbitrary = frequency
+    [ (1, return (Composed Bottom))
+    , (9, Composed . Lift . Op <$> arbitrary)
     ]
