packages feed

monoidmap-internal (empty) → 0.0.0.0

raw patch · 35 files changed

+11177/−0 lines, 35 filesdep +QuickCheckdep +basedep +containers

Dependencies added: QuickCheck, base, containers, deepseq, groups, hspec, monoid-subclasses, monoidmap-internal, nothunks, pretty-show, quickcheck-classes, quickcheck-groups, quickcheck-monoid-subclasses, quickcheck-quid, tasty-bench, tasty-hunit, text

Files

+ CHANGELOG.md view
@@ -0,0 +1,3 @@+# 0.0.0.0++- Initial release.
+ LICENSE view
@@ -0,0 +1,201 @@+                                 Apache License+                           Version 2.0, January 2004+                        http://www.apache.org/licenses/++   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION++   1. Definitions.++      "License" shall mean the terms and conditions for use, reproduction,+      and distribution as defined by Sections 1 through 9 of this document.++      "Licensor" shall mean the copyright owner or entity authorized by+      the copyright owner that is granting the License.++      "Legal Entity" shall mean the union of the acting entity and all+      other entities that control, are controlled by, or are under common+      control with that entity. For the purposes of this definition,+      "control" means (i) the power, direct or indirect, to cause the+      direction or management of such entity, whether by contract or+      otherwise, or (ii) ownership of fifty percent (50%) or more of the+      outstanding shares, or (iii) beneficial ownership of such entity.++      "You" (or "Your") shall mean an individual or Legal Entity+      exercising permissions granted by this License.++      "Source" form shall mean the preferred form for making modifications,+      including but not limited to software source code, documentation+      source, and configuration files.++      "Object" form shall mean any form resulting from mechanical+      transformation or translation of a Source form, including but+      not limited to compiled object code, generated documentation,+      and conversions to other media types.++      "Work" shall mean the work of authorship, whether in Source or+      Object form, made available under the License, as indicated by a+      copyright notice that is included in or attached to the work+      (an example is provided in the Appendix below).++      "Derivative Works" shall mean any work, whether in Source or Object+      form, that is based on (or derived from) the Work and for which the+      editorial revisions, annotations, elaborations, or other modifications+      represent, as a whole, an original work of authorship. For the purposes+      of this License, Derivative Works shall not include works that remain+      separable from, or merely link (or bind by name) to the interfaces of,+      the Work and Derivative Works thereof.++      "Contribution" shall mean any work of authorship, including+      the original version of the Work and any modifications or additions+      to that Work or Derivative Works thereof, that is intentionally+      submitted to Licensor for inclusion in the Work by the copyright owner+      or by an individual or Legal Entity authorized to submit on behalf of+      the copyright owner. For the purposes of this definition, "submitted"+      means any form of electronic, verbal, or written communication sent+      to the Licensor or its representatives, including but not limited to+      communication on electronic mailing lists, source code control systems,+      and issue tracking systems that are managed by, or on behalf of, the+      Licensor for the purpose of discussing and improving the Work, but+      excluding communication that is conspicuously marked or otherwise+      designated in writing by the copyright owner as "Not a Contribution."++      "Contributor" shall mean Licensor and any individual or Legal Entity+      on behalf of whom a Contribution has been received by Licensor and+      subsequently incorporated within the Work.++   2. Grant of Copyright License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      copyright license to reproduce, prepare Derivative Works of,+      publicly display, publicly perform, sublicense, and distribute the+      Work and such Derivative Works in Source or Object form.++   3. Grant of Patent License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      (except as stated in this section) patent license to make, have made,+      use, offer to sell, sell, import, and otherwise transfer the Work,+      where such license applies only to those patent claims licensable+      by such Contributor that are necessarily infringed by their+      Contribution(s) alone or by combination of their Contribution(s)+      with the Work to which such Contribution(s) was submitted. If You+      institute patent litigation against any entity (including a+      cross-claim or counterclaim in a lawsuit) alleging that the Work+      or a Contribution incorporated within the Work constitutes direct+      or contributory patent infringement, then any patent licenses+      granted to You under this License for that Work shall terminate+      as of the date such litigation is filed.++   4. Redistribution. You may reproduce and distribute copies of the+      Work or Derivative Works thereof in any medium, with or without+      modifications, and in Source or Object form, provided that You+      meet the following conditions:++      (a) You must give any other recipients of the Work or+          Derivative Works a copy of this License; and++      (b) You must cause any modified files to carry prominent notices+          stating that You changed the files; and++      (c) You must retain, in the Source form of any Derivative Works+          that You distribute, all copyright, patent, trademark, and+          attribution notices from the Source form of the Work,+          excluding those notices that do not pertain to any part of+          the Derivative Works; and++      (d) If the Work includes a "NOTICE" text file as part of its+          distribution, then any Derivative Works that You distribute must+          include a readable copy of the attribution notices contained+          within such NOTICE file, excluding those notices that do not+          pertain to any part of the Derivative Works, in at least one+          of the following places: within a NOTICE text file distributed+          as part of the Derivative Works; within the Source form or+          documentation, if provided along with the Derivative Works; or,+          within a display generated by the Derivative Works, if and+          wherever such third-party notices normally appear. The contents+          of the NOTICE file are for informational purposes only and+          do not modify the License. You may add Your own attribution+          notices within Derivative Works that You distribute, alongside+          or as an addendum to the NOTICE text from the Work, provided+          that such additional attribution notices cannot be construed+          as modifying the License.++      You may add Your own copyright statement to Your modifications and+      may provide additional or different license terms and conditions+      for use, reproduction, or distribution of Your modifications, or+      for any such Derivative Works as a whole, provided Your use,+      reproduction, and distribution of the Work otherwise complies with+      the conditions stated in this License.++   5. Submission of Contributions. Unless You explicitly state otherwise,+      any Contribution intentionally submitted for inclusion in the Work+      by You to the Licensor shall be under the terms and conditions of+      this License, without any additional terms or conditions.+      Notwithstanding the above, nothing herein shall supersede or modify+      the terms of any separate license agreement you may have executed+      with Licensor regarding such Contributions.++   6. Trademarks. This License does not grant permission to use the trade+      names, trademarks, service marks, or product names of the Licensor,+      except as required for reasonable and customary use in describing the+      origin of the Work and reproducing the content of the NOTICE file.++   7. Disclaimer of Warranty. Unless required by applicable law or+      agreed to in writing, Licensor provides the Work (and each+      Contributor provides its Contributions) on an "AS IS" BASIS,+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or+      implied, including, without limitation, any warranties or conditions+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A+      PARTICULAR PURPOSE. You are solely responsible for determining the+      appropriateness of using or redistributing the Work and assume any+      risks associated with Your exercise of permissions under this License.++   8. Limitation of Liability. In no event and under no legal theory,+      whether in tort (including negligence), contract, or otherwise,+      unless required by applicable law (such as deliberate and grossly+      negligent acts) or agreed to in writing, shall any Contributor be+      liable to You for damages, including any direct, indirect, special,+      incidental, or consequential damages of any character arising as a+      result of this License or out of the use or inability to use the+      Work (including but not limited to damages for loss of goodwill,+      work stoppage, computer failure or malfunction, or any and all+      other commercial damages or losses), even if such Contributor+      has been advised of the possibility of such damages.++   9. Accepting Warranty or Additional Liability. While redistributing+      the Work or Derivative Works thereof, You may choose to offer,+      and charge a fee for, acceptance of support, warranty, indemnity,+      or other liability obligations and/or rights consistent with this+      License. However, in accepting such obligations, You may act only+      on Your own behalf and on Your sole responsibility, not on behalf+      of any other Contributor, and only if You agree to indemnify,+      defend, and hold each Contributor harmless for any liability+      incurred by, or claims asserted against, such Contributor by reason+      of your accepting any such warranty or additional liability.++   END OF TERMS AND CONDITIONS++   APPENDIX: How to apply the Apache License to your work.++      To apply the Apache License to your work, attach the following+      boilerplate notice, with the fields enclosed by brackets "[]"+      replaced with your own identifying information. (Don't include+      the brackets!)  The text should be enclosed in the appropriate+      comment syntax for the file format. We also recommend that a+      file or class name and description of purpose be included on the+      same "printed page" as the copyright notice for easier+      identification within third-party archives.++   Copyright © 2022–2025 Jonathan Knowles++   Licensed under the Apache License, Version 2.0 (the "License");+   you may not use this file except in compliance with the License.+   You may obtain a copy of the License at++       http://www.apache.org/licenses/LICENSE-2.0++   Unless required by applicable law or agreed to in writing, software+   distributed under the License is distributed on an "AS IS" BASIS,+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+   See the License for the specific language governing permissions and+   limitations under the License.
+ README.md view
@@ -0,0 +1,1 @@+Internal support for the [`monoidmap`](https://github.com/jonathanknowles/monoidmap) package.
+ components/monoidmap-benchmark/Main.hs view
@@ -0,0 +1,205 @@+-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+-- Benchmark for the `MonoidMap` type.+--+-- Instead of benchmarking functions for the `MonoidMap` type directly, we+-- benchmark functions for the `RecoveredMap` type, a newtype wrapper around+-- the `MonoidMap` type designed to provide the same semantics as `Map`.+--+module Main where++import Control.DeepSeq+    ( rnf )+import Control.Exception+    ( evaluate )+import Data.Eq+    ( Eq )+import Data.Function+    ( flip, ($) )+import Data.Int+    ( Int )+import Data.List+    ( foldl', zip )+import Data.Maybe+    ( Maybe, fromMaybe )+import Data.Ord+    ( Ord )+import Data.Semigroup+    ( Semigroup ((<>)), stimes )+import Prelude+    ( Integer, Num, (^), (+) )+import System.IO+    ( IO )+import Test.Tasty.Bench+    ( bench, bgroup, defaultMain, nf )++import qualified Data.Map.Strict as OMap+import qualified Data.MonoidMap.Internal.RecoveredMap as RMap++main :: IO ()+main = do++    let om_natural = fromList elems_natural :: OMap.Map Int Int+        om_even    = fromList elems_even    :: OMap.Map Int Int+        om_odd     = fromList elems_odd     :: OMap.Map Int Int++        rm_natural = fromList elems_natural :: RMap.Map Int Int+        rm_even    = fromList elems_even    :: RMap.Map Int Int+        rm_odd     = fromList elems_odd     :: RMap.Map Int Int++    evaluate $ rnf [om_natural, om_even, om_odd]+    evaluate $ rnf [rm_natural, rm_even, rm_odd]++    defaultMain+        [ bgroup "delete"+            [ bgroup "absent"+                [ bench "Data.Map.Strict" $+                    nf (deleteMany evens) om_odd+                , bench "RecoveredMap" $+                    nf (deleteMany evens) rm_odd+                ]+            , bgroup "present"+                [ bench "Data.Map.Strict" $+                    nf (deleteMany evens) om_even+                , bench "RecoveredMap" $+                    nf (deleteMany evens) rm_even+                ]+            ]+        , bgroup "insert"+            [ bgroup "absent"+                [ bench "Data.Map.Strict" $+                    nf (insertMany elems_even) om_odd+                , bench "RecoveredMap" $+                    nf (insertMany elems_even) rm_odd+                ]+            , bgroup "present"+                [ bench "Data.Map.Strict" $+                    nf (insertMany elems_even) om_even+                , bench "RecoveredMap" $+                    nf (insertMany elems_even) rm_even+                ]+            ]+        , bgroup "lookup"+            [ bgroup "absent"+                [ bench "Data.Map.Strict" $+                    nf (lookupMany evens) om_odd+                , bench "RecoveredMap" $+                    nf (lookupMany evens) rm_odd+                ]+            , bgroup "present"+                [ bench "Data.Map.Strict" $+                    nf (lookupMany evens) om_even+                , bench "RecoveredMap" $+                    nf (lookupMany evens) rm_even+                ]+            ]+        , bgroup "mappend"+            [ bgroup "disjoint"+                [ bench "Data.Map.Strict" $+                    nf (<> om_even) om_odd+                , bench "RecoveredMap" $+                    nf (<> rm_even) rm_odd+                ]+            , bgroup "identical"+                [ bench "Data.Map.Strict" $+                    nf (<> om_even) om_even+                , bench "RecoveredMap" $+                    nf (<> rm_even) rm_even+                ]+            ]+        , bgroup "stimes"+            [ bench "Data.Map.Strict" $+                nf (stimes ten_power_24) om_natural+            , bench "RecoveredMap" $+                nf (stimes ten_power_24) rm_natural+            ]+        , bgroup "mapAccumL"+            [ bench "Data.Map.Strict" $+                nf (mapAccumL (\s v -> (s + v, v)) 0) om_natural+            , bench "RecoveredMap" $+                nf (mapAccumL (\s v -> (s + v, v)) 0) rm_natural+            ]+        , bgroup "mapAccumR"+            [ bench "Data.Map.Strict" $+                nf (mapAccumR (\s v -> (s + v, v)) 0) om_natural+            , bench "RecoveredMap" $+                nf (mapAccumR (\s v -> (s + v, v)) 0) rm_natural+            ]+        , bgroup "mapAccumLWithKey"+            [ bench "Data.Map.Strict" $+                nf (mapAccumL (\s v -> (s + v, v)) 0) om_natural+            , bench "RecoveredMap" $+                nf (mapAccumL (\s v -> (s + v, v)) 0) rm_natural+            ]+        , bgroup "mapAccumRWithKey"+            [ bench "Data.Map.Strict" $+                nf (mapAccumRWithKey (\s k v -> (s + k + v, v)) 0) om_natural+            , bench "RecoveredMap" $+                nf (mapAccumRWithKey (\s k v -> (s + k + v, v)) 0) rm_natural+            ]+        ]+  where+    bound :: Int+    bound = 2 ^ (16 :: Int)++    elems_natural :: [(Int, Int)]+    elems_natural = zip naturals naturals++    elems_even :: [(Int, Int)]+    elems_even = zip evens evens++    elems_odd :: [(Int, Int)]+    elems_odd = zip odds odds++    naturals :: [Int]+    naturals = [1 .. bound]++    evens :: [Int]+    evens = [2, 4 .. bound]++    odds :: [Int]+    odds = [1, 3 .. bound]++    ten_power_24 :: Integer+    ten_power_24 = 1_000_000_000_000_000_000_000_000++class Ord k => Map m k v where+    fromList :: [(k, v)] -> m k v+    delete :: k -> m k v -> m k v+    insert :: k -> v -> m k v -> m k v+    lookup :: k -> m k v -> Maybe v+    mapAccumL :: (s -> v -> (s, v)) -> s -> m k v -> (s, m k v)+    mapAccumR :: (s -> v -> (s, v)) -> s -> m k v -> (s, m k v)+    mapAccumLWithKey :: (s -> k -> v -> (s, v)) -> s -> m k v -> (s, m k v)+    mapAccumRWithKey :: (s -> k -> v -> (s, v)) -> s -> m k v -> (s, m k v)++instance Ord k => Map OMap.Map k v where+    fromList = OMap.fromList+    delete = OMap.delete+    insert = OMap.insert+    lookup = OMap.lookup+    mapAccumL = OMap.mapAccum+    mapAccumR f = OMap.mapAccumRWithKey (\s _ v -> f s v)+    mapAccumLWithKey = OMap.mapAccumWithKey+    mapAccumRWithKey = OMap.mapAccumRWithKey++instance (Ord k, Eq v) => Map RMap.Map k v where+    fromList = RMap.fromList+    delete = RMap.delete+    insert = RMap.insert+    lookup = RMap.lookup+    mapAccumL = RMap.mapAccumL+    mapAccumR = RMap.mapAccumR+    mapAccumLWithKey = RMap.mapAccumLWithKey+    mapAccumRWithKey = RMap.mapAccumRWithKey++deleteMany :: (Map m k v, Num v) => [k] -> m k v -> m k v+deleteMany xs m = foldl' (flip delete) m xs++insertMany :: (Map m k v, Num v) => [(k, v)] -> m k v -> m k v+insertMany xs m = foldl' (\m' (k, v) -> insert k v m') m xs++lookupMany :: (Map m k v, Num v) => [k] -> m k v -> v+lookupMany xs m = foldl' (\n k -> fromMaybe n (lookup k m)) 0 xs
+ components/monoidmap-internal/Data/MonoidMap/Internal.hs view
@@ -0,0 +1,3521 @@+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}+{-# OPTIONS_GHC -fno-warn-unused-imports #-}+{-# OPTIONS_HADDOCK not-home #-}+{- HLINT ignore "Avoid lambda" -}+{- HLINT ignore "Avoid lambda using `infix`" -}+{- HLINT ignore "Redundant bracket" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+-- Provides /internal/ operations for the 'MonoidMap' type.+--+module Data.MonoidMap.Internal+    (+    -- * Types+      MonoidMap (..)+    , NonNull (..)++    -- * General operations++    -- ** Construction+    , empty+    , fromList+    , fromListWith+    , fromMap+    , fromMapWith+    , fromSet+    , singleton++    -- ** Deconstruction+    , toList+    , toMap++    -- ** Lookup+    , get++    -- ** Modification+    , set+    , adjust+    , nullify++    -- ** Membership+    , null+    , nullKey+    , nonNull+    , nonNullCount+    , nonNullKey+    , nonNullKeys++    -- ** Slicing+    , take+    , drop+    , splitAt++    -- ** Filtering+    , filter+    , filterKeys+    , filterWithKey++    -- ** Partitioning+    , partition+    , partitionKeys+    , partitionWithKey++    -- ** Mapping+    , map+    , mapKeys+    , mapKeysWith+    , mapWithKey++    -- ** Folding+    , foldl+    , foldl'+    , foldr+    , foldr'+    , foldlWithKey+    , foldlWithKey'+    , foldrWithKey+    , foldrWithKey'+    , foldMapWithKey+    , foldMapWithKey'++    -- ** Traversal+    , traverse+    , traverseWithKey+    , mapAccumL+    , mapAccumLWithKey+    , mapAccumR+    , mapAccumRWithKey++    -- * Monoidal operations++    -- ** Association+    , append++    -- ** Subtraction+    , minus+    , minusMaybe+    , monus++    -- ** Inversion+    , invert++    -- ** Exponentiation+    , power++    -- ** Comparison+    , isSubmapOf+    , isSubmapOfBy+    , disjoint+    , disjointBy++    -- ** Intersection+    , intersection+    , intersectionWith+    , intersectionWithA++    -- ** Union+    , union+    , unionWith+    , unionWithA++    -- ** Prefixes+    , isPrefixOf+    , stripPrefix+    , commonPrefix+    , stripCommonPrefix++    -- ** Suffixes+    , isSuffixOf+    , stripSuffix+    , commonSuffix+    , stripCommonSuffix++    -- ** Overlap+    , overlap+    , stripPrefixOverlap+    , stripSuffixOverlap+    , stripOverlap+    )+    where++import Prelude hiding+    ( drop+    , filter+    , foldl+    , foldl'+    , foldr+    , lookup+    , map+    , null+    , splitAt+    , subtract+    , take+    , traverse+    )++import Control.Applicative+    ( Applicative (..) )+import Control.DeepSeq+    ( NFData )+import Data.Bifoldable+    ( Bifoldable )+import Data.Coerce+    ( coerce )+import Data.Function+    ( (&) )+import Data.Functor.Classes+    ( Eq1, Eq2, Show1, Show2 )+import Data.Functor.Identity+    ( Identity (..) )+import Data.Group+    ( Abelian, Group )+import Data.Map.Strict+    ( Map, lookup )+import Data.Maybe+    ( fromMaybe, isJust )+import Data.Monoid.GCD+    ( DistributiveGCDMonoid+    , GCDMonoid+    , LeftDistributiveGCDMonoid+    , LeftGCDMonoid+    , OverlappingGCDMonoid+    , RightDistributiveGCDMonoid+    , RightGCDMonoid+    )+import Data.Monoid.LCM+    ( DistributiveLCMMonoid, LCMMonoid )+import Data.Monoid.Monus+    ( Monus (..) )+import Data.Monoid.Null+    ( MonoidNull, PositiveMonoid )+import Data.Semigroup+    ( stimes )+import Data.Semigroup.Cancellative+    ( Cancellative+    , Commutative+    , LeftCancellative+    , LeftReductive+    , Reductive (..)+    , RightCancellative+    , RightReductive+    )+import Data.Set+    ( Set )+import GHC.Exts+    ( IsList (Item) )+import NoThunks.Class+    ( NoThunks )+import Text.Read+    ( Read (..) )++import qualified Data.Bifunctor as B+import qualified Data.Foldable as F+import qualified Data.List as L+import qualified Data.List.NonEmpty as NE+import qualified Data.Map.Merge.Strict as Map+import qualified Data.Map.Strict as Map+import qualified Data.Set as Set+import qualified GHC.Exts as GHC+import qualified Data.Traversable as Traversable++import qualified Data.Group as C+import qualified Data.Monoid.GCD as C+import qualified Data.Monoid.LCM as C+import qualified Data.Monoid.Null as C+import qualified Data.Semigroup.Cancellative as C++--------------------------------------------------------------------------------+-- Type+--------------------------------------------------------------------------------++newtype MonoidMap k v = MonoidMap (Map k (NonNull v))+    deriving (Eq, Show, NFData, NoThunks)+        via Map k v+    deriving (Eq1, Show1, Foldable)+        via Map k+    deriving (Eq2, Show2, Bifoldable)+        via Map++-- Internal alias used when extra brevity is required.+type MM = MonoidMap++--------------------------------------------------------------------------------+-- Non-null values+--------------------------------------------------------------------------------++newtype NonNull v = UnsafeNonNull {getNonNull :: v}++maybeNonNull :: MonoidNull v => v -> Maybe (NonNull v)+maybeNonNull !v+    | C.null  v = Nothing+    | otherwise = Just (UnsafeNonNull v)+{-# INLINE maybeNonNull #-}++applyNonNull :: (v -> a) -> (NonNull v -> a)+applyNonNull = coerce+{-# INLINE applyNonNull #-}++applyNonNull2 :: (v1 -> v2 -> a) -> (NonNull v1 -> NonNull v2 -> a)+applyNonNull2 = coerce+{-# INLINE applyNonNull2 #-}++--------------------------------------------------------------------------------+-- Instances+--------------------------------------------------------------------------------++instance (Ord k, MonoidNull v) =>+    IsList (MonoidMap k v)+  where+    type Item (MonoidMap k v) = (k, v)+    fromList = fromList+    toList = toList++instance (Ord k, Read k, MonoidNull v, Read v) =>+    Read (MonoidMap k v)+  where+    readPrec = fromMap <$> readPrec++--------------------------------------------------------------------------------+-- Instances: Semigroup and subclasses+--------------------------------------------------------------------------------++instance (Ord k, MonoidNull v) =>+    Semigroup (MonoidMap k v)+  where+    (<>) = append+    stimes 0 = const mempty+    stimes 1 = id+    stimes n = map (stimes n)++instance (Ord k, MonoidNull v, Commutative v) =>+    Commutative (MonoidMap k v)++instance (Ord k, MonoidNull v, LeftReductive v) =>+    LeftReductive (MonoidMap k v)+  where+    isPrefixOf = isPrefixOf+    stripPrefix = stripPrefix++instance (Ord k, MonoidNull v, RightReductive v) =>+    RightReductive (MonoidMap k v)+  where+    isSuffixOf = isSuffixOf+    stripSuffix = stripSuffix++instance (Ord k, MonoidNull v, Reductive v) =>+    Reductive (MonoidMap k v)+  where+    (</>) = minusMaybe++instance (Ord k, MonoidNull v, LeftCancellative v) =>+    LeftCancellative (MonoidMap k v)++instance (Ord k, MonoidNull v, RightCancellative v) =>+    RightCancellative (MonoidMap k v)++instance (Ord k, MonoidNull v, Cancellative v) =>+    Cancellative (MonoidMap k v)++--------------------------------------------------------------------------------+-- Instances: Monoid and subclasses+--------------------------------------------------------------------------------++instance (Ord k, MonoidNull v) =>+    Monoid (MonoidMap k v)+  where+    mempty = empty++instance (Ord k, MonoidNull v) =>+    MonoidNull (MonoidMap k v)+  where+    null = null++instance (Ord k, PositiveMonoid v) =>+    PositiveMonoid (MonoidMap k v)++instance (Ord k, MonoidNull v, LeftGCDMonoid v) =>+    LeftGCDMonoid (MonoidMap k v)+  where+    commonPrefix = commonPrefix++instance (Ord k, MonoidNull v, LeftDistributiveGCDMonoid v) =>+    LeftDistributiveGCDMonoid (MonoidMap k v)++instance (Ord k, MonoidNull v, RightGCDMonoid v) =>+    RightGCDMonoid (MonoidMap k v)+  where+    commonSuffix = commonSuffix++instance (Ord k, MonoidNull v, RightDistributiveGCDMonoid v) =>+    RightDistributiveGCDMonoid (MonoidMap k v)++instance (Ord k, MonoidNull v, OverlappingGCDMonoid v) =>+    OverlappingGCDMonoid (MonoidMap k v)+  where+    overlap = overlap+    stripPrefixOverlap = stripPrefixOverlap+    stripSuffixOverlap = stripSuffixOverlap+    stripOverlap = stripOverlap++instance (Ord k, MonoidNull v, GCDMonoid v) =>+    GCDMonoid (MonoidMap k v)+  where+    gcd = intersection++instance (Ord k, MonoidNull v, DistributiveGCDMonoid v) =>+    DistributiveGCDMonoid (MonoidMap k v)++instance (Ord k, MonoidNull v, LCMMonoid v) =>+    LCMMonoid (MonoidMap k v)+  where+    lcm = union++instance (Ord k, MonoidNull v, DistributiveLCMMonoid v) =>+    DistributiveLCMMonoid (MonoidMap k v)++instance (Ord k, MonoidNull v, Monus v) =>+    Monus (MonoidMap k v)+  where+    (<\>) = monus++--------------------------------------------------------------------------------+-- Instances: Group and subclasses+--------------------------------------------------------------------------------++instance (Ord k, MonoidNull v, Group v) =>+    Group (MonoidMap k v)+  where+    invert = invert+    (~~) = minus+    pow = power++instance (Ord k, MonoidNull v, Abelian v) =>+    Abelian (MonoidMap k v)++--------------------------------------------------------------------------------+-- Construction+--------------------------------------------------------------------------------++-- | \(O(1)\). The empty 'MonoidMap'.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k 'empty' '==' 'mempty'+-- @+--+-- Provides the definition of 'mempty' for the 'MonoidMap' instance of+-- 'Monoid'.+--+empty :: MonoidMap k v+empty = MonoidMap Map.empty++-- | \(O(n \log n)\). Constructs a 'MonoidMap' from a list of key-value pairs.+--+-- If the list contains more than one value for the same key, values are+-- combined together in the order that they appear with the '(<>)' operator.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k ('fromList' kvs) '=='+--     'foldMap' 'snd' ('L.filter' (('==' k) . fst) kvs)+-- @+--+-- Satisfies the following round-trip property:+--+-- @+-- 'fromList' ('toList' m) '==' m+-- @+--+-- === __Examples__+--+-- With 'String' values:+--+-- @+-- >>> 'fromList' [(1,"a"), (2,"x"), (1,"b"), (2,"y"), (1,"c"), (2,"z")]+-- 'fromList' [(1,"abc"), (2,"xyz")]+-- @+--+fromList :: (Ord k, MonoidNull v) => [(k, v)] -> MonoidMap k v+fromList = fromListWith (<>)++-- | \(O(n \log n)\). Constructs a 'MonoidMap' from a list of key-value pairs,+--   with a combining function for values.+--+-- If the list contains more than one value for the same key, values are+-- combined together in the order that they appear with the given combining+-- function.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k ('fromListWith' f kvs) '=='+--     'maybe' 'mempty' ('F.foldl1' f)+--         ('NE.nonEmpty' ('snd' '<$>' 'L.filter' (('==' k) . fst) kvs))+-- @+--+fromListWith+    :: (Ord k, MonoidNull v)+    => (v -> v -> v)+    -- ^ Function with which to combine values for duplicate keys.+    -> [(k, v)]+    -> MonoidMap k v+fromListWith f =+    -- The 'Map.fromListWith' function combines values for duplicate keys in+    -- /reverse order/, so we must flip the provided combining function.+    fromMap . Map.fromListWith (flip f)++-- | \(O(n)\). Constructs a 'MonoidMap' from an ordinary 'Map'.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k ('fromMap' m) '==' 'Map'.'Map.findWithDefault' 'mempty' k m+-- @+--+-- This function performs canonicalisation of 'C.null' values, and has a time+-- complexity that is linear in the size of the map.+--+fromMap :: MonoidNull v => Map k v -> MonoidMap k v+fromMap = MonoidMap . Map.mapMaybe maybeNonNull++-- | \(O(n)\). Constructs a 'MonoidMap' from an ordinary 'Map', applying+--   the given function to all values.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k ('fromMapWith' f m) '==' 'maybe' 'mempty' f ('Map'.'Map.lookup' k m)+-- @+--+-- This function performs canonicalisation of 'C.null' values, and has a time+-- complexity that is linear in the size of the map.+--+-- @since 0.0.4.0+--+fromMapWith :: MonoidNull v2 => (v1 -> v2) -> Map k v1 -> MonoidMap k v2+fromMapWith f = MonoidMap . Map.mapMaybe (maybeNonNull . f)++-- | \(O(n)\). Constructs a 'MonoidMap' from a 'Set' and a function from+--   keys to values.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k ('fromSet' f ks) '=='+--     if 'Set'.'Set.member' k ks+--     then f k+--     else 'mempty'+-- @+--+-- This function performs canonicalisation of 'C.null' values, and has a time+-- complexity that is linear in the 'Set.size' of the set.+--+-- @since 0.0.2.0+--+fromSet :: MonoidNull v => (k -> v) -> Set k -> MonoidMap k v+fromSet f = fromMap . Map.fromSet f++-- | \(O(1)\). Constructs a 'MonoidMap' from a single key-value pair.+--+-- Satisfies the following property:+--+-- @+-- 'get' k ('singleton' k v) '==' v+-- @+--+-- Nullifying the value for key __@k@__ produces an 'empty' map:+--+-- @+-- 'nullify' k ('singleton' k v) '==' 'empty'+-- @+--+singleton :: (Ord k, MonoidNull v) => k -> v -> MonoidMap k v+singleton k v = set k v mempty++--------------------------------------------------------------------------------+-- Deconstruction+--------------------------------------------------------------------------------++-- | \(O(n)\). Converts a 'MonoidMap' to a list of key-value pairs, where the+--   keys are in ascending order.+--+-- The result only includes entries with values that are not 'C.null'.+--+-- Satisfies the following round-trip property:+--+-- @+-- 'fromList' ('toList' m) '==' m+-- @+--+-- The resulting list is sorted in ascending key order:+--+-- @+-- 'L.sortOn' 'fst' ('toList' m) '==' 'toList' m+-- @+--+toList :: MonoidMap k v -> [(k, v)]+toList = Map.toAscList . toMap++-- | \(O(1)\). Converts a 'MonoidMap' to an ordinary 'Map'.+--+-- The result only includes entries with values that are not 'C.null'.+--+-- Satisfies the following round-trip property:+--+-- @+-- 'fromMap' ('toMap' m) '==' m+-- @+--+toMap :: forall k v. MonoidMap k v -> Map k v+toMap = coerce++--------------------------------------------------------------------------------+-- Lookup+--------------------------------------------------------------------------------++-- | \(O(\log n)\). Gets the value associated with the given key.+--+-- By default, every key in an 'empty' map is associated with a value of+-- 'mempty':+--+-- @+-- ∀ k. 'get' k 'empty' '==' 'mempty'+-- @+--+get :: (Ord k, Monoid v) => k -> MonoidMap k v -> v+get k m = fromMaybe mempty $ Map.lookup k $ toMap m++--------------------------------------------------------------------------------+-- Modification+--------------------------------------------------------------------------------++-- | \(O(\log n)\). Sets the value associated with the given key.+--+-- Satisfies the following property:+--+-- @+-- 'get' k ('set' k v m) '==' v+-- @+--+set :: (Ord k, MonoidNull v) => k -> v -> MonoidMap k v -> MonoidMap k v+set k v (MonoidMap m) = MonoidMap $ case maybeNonNull v of+    Just v0 -> Map.insert k v0 m+    Nothing -> Map.delete k    m++-- | \(O(\log n)\). Adjusts the value associated with the given key.+--+-- Satisfies the following property:+--+-- @+-- 'adjust' f k m '==' 'set' k (f ('get' k m)) m+-- @+--+adjust+    :: (Ord k, MonoidNull v)+    => (v -> v)+    -> k+    -> MonoidMap k v+    -> MonoidMap k v+adjust f k (MonoidMap m) = MonoidMap $+    Map.alter (maybeNonNull . maybe (f mempty) (applyNonNull f)) k m++-- | \(O(\log n)\). Sets the value associated with the given key to 'mempty'.+--+-- Satisfies the following property:+--+-- @+-- 'get' k ('nullify' k m) '==' 'mempty'+-- @+--+nullify :: Ord k => k -> MonoidMap k v -> MonoidMap k v+nullify k (MonoidMap m) = MonoidMap $ Map.delete k m++--------------------------------------------------------------------------------+-- Membership+--------------------------------------------------------------------------------++-- | \(O(1)\). Returns 'True' if (and only if) all values in the map are+--   'C.null'.+--+-- Satisfies the following property:+--+-- @+-- 'null' m '==' (∀ k. 'nullKey' k m)+-- @+--+-- Provides the definition of 'C.null' for the 'MonoidMap' instance of+-- 'MonoidNull'.+--+null :: MonoidMap k v -> Bool+null = Map.null . toMap++-- | \(O(\log n)\). Returns 'True' if (and only if) the given key is associated+--   with a value that is 'C.null'.+--+-- Satisfies the following property:+--+-- @+-- 'nullKey' k m '==' 'C.null' ('get' k m)+-- @+--+nullKey :: Ord k => k -> MonoidMap k v -> Bool+nullKey k = Map.notMember k . toMap++-- | \(O(1)\). Returns 'True' if (and only if) the map contains at least one+--   value that is not 'C.null'.+--+-- Satisfies the following property:+--+-- @+-- 'nonNull' m '==' (∃ k. 'nonNullKey' k m)+-- @+--+nonNull :: MonoidMap k v -> Bool+nonNull = not . null++-- | \(O(1)\). Returns a count of all values in the map that are not 'C.null'.+--+-- Satisfies the following property:+--+-- @+-- 'nonNullCount' m '==' 'Set.size' ('nonNullKeys' m)+-- @+--+nonNullCount :: MonoidMap k v -> Int+nonNullCount = Map.size . toMap++-- | \(O(\log n)\). Returns 'True' if (and only if) the given key is associated+--   with a value that is not 'C.null'.+--+-- Satisfies the following property:+--+-- @+-- 'nonNullKey' k m '==' 'not' ('C.null' ('get' k m))+-- @+--+nonNullKey :: Ord k => k -> MonoidMap k v -> Bool+nonNullKey k = Map.member k . toMap++-- | \(O(n)\). Returns the set of keys associated with values that are not+--   'C.null'.+--+-- Satisfies the following property:+--+-- @+-- k '`Set.member`' ('nonNullKeys' m) '==' 'nonNullKey' k m+-- @+--+nonNullKeys :: MonoidMap k v -> Set k+nonNullKeys = Map.keysSet . toMap++--------------------------------------------------------------------------------+-- Slicing+--------------------------------------------------------------------------------++-- | \(O(\log n)\). /Takes/ a slice from a map.+--+-- This function takes a given number of non-'C.null' entries from a map,+-- producing a new map from the entries that were /taken/.+--+-- Entries are taken in /key order/, beginning with the /smallest/ keys.+--+-- Satifies the following property:+--+-- @+-- 'take' n '==' 'fromList' . 'Prelude.take' n . 'toList'+-- @+--+take :: Int -> MonoidMap k v -> MonoidMap k v+take i (MonoidMap m) = MonoidMap (Map.take i m)++-- | \(O(\log n)\). /Drops/ a slice from a map.+--+-- This function drops a given number of non-'C.null' entries from a map,+-- producing a new map from the entries that /remain/.+--+-- Entries are dropped in /key order/, beginning with the /smallest/ keys.+--+-- Satifies the following property:+--+-- @+-- 'drop' n '==' 'fromList' . 'Prelude.drop' n . 'toList'+-- @+--+drop :: Int -> MonoidMap k v -> MonoidMap k v+drop i (MonoidMap m) = MonoidMap (Map.drop i m)++-- | \(O(\log n)\). /Splits/ a map into /two/ slices.+--+-- This function is equivalent to a combination of 'take' and 'drop':+--+-- @+-- 'splitAt' n m '==' ('take' n m, 'drop' n m)+-- @+--+-- The resulting maps can be combined to reproduce the original map:+--+-- @+-- 'splitAt' n m '&'+--     \\(m1, m2) -> m1 '<>' m2 '==' m+-- @+--+-- The resulting maps have disjoint sets of non-'C.null' entries:+--+-- @+-- 'splitAt' n m '&'+--     \\(m1, m2) -> 'Set.disjoint' ('nonNullKeys' m1) ('nonNullKeys' m2)+-- @+--+splitAt :: Int -> MonoidMap k a -> (MonoidMap k a, MonoidMap k a)+splitAt i m = (take i m, drop i m)++--------------------------------------------------------------------------------+-- Filtering+--------------------------------------------------------------------------------++-- | \(O(n)\). Filters a map according to a predicate on /values/.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k ('filter' f m) '=='+--     if f ('get' k m)+--     then 'get' k m+--     else 'mempty'+-- @+--+-- The resulting map is identical to that obtained by constructing a map from a+-- filtered list of key-value pairs:+--+-- @+-- 'filter' f m '==' 'fromList' ('L.filter' (f . 'snd') ('toList' m))+-- @+--+filter :: (v -> Bool) -> MonoidMap k v -> MonoidMap k v+filter f (MonoidMap m) = MonoidMap $ Map.filter (applyNonNull f) m++-- | \(O(n)\). Filters a map according to a predicate on /keys/.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k ('filterKeys' f m) '=='+--     if f k+--     then 'get' k m+--     else 'mempty'+-- @+--+-- The resulting map is identical to that obtained by constructing a map from a+-- filtered list of key-value pairs:+--+-- @+-- 'filter' f m '==' 'fromList' ('L.filter' (f . 'fst') ('toList' m))+-- @+--+filterKeys :: (k -> Bool) -> MonoidMap k v -> MonoidMap k v+filterKeys f (MonoidMap m) = MonoidMap $ Map.filterWithKey (\k _ -> f k) m++-- | \(O(n)\). Filters a map according to a predicate on /keys and values/.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k ('filterWithKey' f m) '=='+--     if f k ('get' k m)+--     then 'get' k m+--     else 'mempty'+-- @+--+-- The resulting map is identical to that obtained by constructing a map from a+-- filtered list of key-value pairs:+--+-- @+-- 'filterWithKey' f m '==' 'fromList' ('L.filter' ('uncurry' f) ('toList' m))+-- @+--+filterWithKey :: (k -> v -> Bool) -> MonoidMap k v -> MonoidMap k v+filterWithKey f (MonoidMap m) =+    MonoidMap $ Map.filterWithKey (applyNonNull . f) m++--------------------------------------------------------------------------------+-- Partitioning+--------------------------------------------------------------------------------++-- | \(O(n)\). Partitions a map according to a predicate on /values/.+--+-- Satisfies the following property:+--+-- @+-- 'partition' f m '=='+--     ( 'filter'  \   \   f  m+--     , 'filter' ('not' . f) m+--     )+-- @+--+-- The resulting maps can be combined to reproduce the original map:+--+-- @+-- 'partition' f m '&' \\(m1, m2) ->+--     m1 '<>' m2 '==' m+-- @+--+-- The resulting maps have disjoint sets of non-'C.null' entries:+--+-- @+-- 'partition' f m '&' \\(m1, m2) ->+--     'Set.disjoint'+--         ('nonNullKeys' m1)+--         ('nonNullKeys' m2)+-- @+--+partition :: (v -> Bool) -> MonoidMap k v -> (MonoidMap k v, MonoidMap k v)+partition f (MonoidMap m) =+    B.bimap MonoidMap MonoidMap $ Map.partition (applyNonNull f) m++-- | \(O(n)\). Partitions a map according to a predicate on /keys/.+--+-- Satisfies the following property:+--+-- @+-- 'partitionKeys' f m '=='+--     ( 'filterKeys'  \   \   f  m+--     , 'filterKeys' ('not' . f) m+--     )+-- @+--+-- The resulting maps can be combined to reproduce the original map:+--+-- @+-- 'partitionKeys' f m '&' \\(m1, m2) ->+--     m1 '<>' m2 '==' m+-- @+--+-- The resulting maps have disjoint sets of non-'C.null' entries:+--+-- @+-- 'partitionKeys' f m '&' \\(m1, m2) ->+--     'Set.disjoint'+--         ('nonNullKeys' m1)+--         ('nonNullKeys' m2)+-- @+--+partitionKeys+    :: (k -> Bool) -> MonoidMap k v -> (MonoidMap k v, MonoidMap k v)+partitionKeys f (MonoidMap m) =+    B.bimap MonoidMap MonoidMap $ Map.partitionWithKey (\k _ -> f k) m++-- | \(O(n)\). Partitions a map according to a predicate on /keys and values/.+--+-- Satisfies the following property:+--+-- @+-- 'partitionWithKey' f m '=='+--     ( 'filterWithKey'   \    \   \    \  \   \ f  m+--     , 'filterWithKey' (('fmap' . 'fmap') 'not' f) m+--     )+-- @+--+-- The resulting maps can be combined to reproduce the original map:+--+-- @+-- 'partitionWithKey' f m '&' \\(m1, m2) ->+--     m1 '<>' m2 '==' m+-- @+--+-- The resulting maps have disjoint sets of non-'C.null' entries:+--+-- @+-- 'partitionWithKey' f m '&' \\(m1, m2) ->+--     'Set.disjoint'+--         ('nonNullKeys' m1)+--         ('nonNullKeys' m2)+-- @+--+partitionWithKey+    :: (k -> v -> Bool) -> MonoidMap k v -> (MonoidMap k v, MonoidMap k v)+partitionWithKey f (MonoidMap m) =+    B.bimap MonoidMap MonoidMap $ Map.partitionWithKey (applyNonNull . f) m++--------------------------------------------------------------------------------+-- Mapping+--------------------------------------------------------------------------------++-- | \(O(n)\). Applies a function to all non-'C.null' values of a 'MonoidMap'.+--+-- Satisfies the following properties for all functions __@f@__:+--+-- @+-- ('get' k m '==' 'mempty') ==> ('get' k ('map' f m) '==' 'mempty'     )+-- ('get' k m '/=' 'mempty') ==> ('get' k ('map' f m) '==' f ('get' k m))+-- @+--+-- === Conditional properties+--+-- If applying function __@f@__ to 'mempty' produces 'mempty', then the+-- following additional properties hold:+--+-- @+-- (f 'mempty' '==' 'mempty')+--     ==>+--     (∀ k. 'get' k ('map' f m) '==' f ('get' k m))+-- @+--+-- @+-- (f 'mempty' '==' 'mempty')+--     ==>+--     (∀ g. 'map' (f . g) m '==' 'map' f ('map' g m))+-- @+--+map+    :: MonoidNull v2+    => (v1 -> v2)+    -> MonoidMap k v1+    -> MonoidMap k v2+map f (MonoidMap m) =+    MonoidMap $ Map.mapMaybe (maybeNonNull . applyNonNull f) m++-- | \(O(n \log n)\). Applies a function to all the keys of a 'MonoidMap' that+--   are associated with non-'C.null' values.+--+-- If the resultant map would contain more than one value for the same key,+-- values are combined together in ascending key order with the '(<>)'+-- operator.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k ('mapKeys' f m) '=='+--     'F.foldMap'+--         ('`get`' m)+--         ('Set.filter' (('==') k . f) ('nonNullKeys' m))+-- @+--+mapKeys+    :: (Ord k2, MonoidNull v)+    => (k1 -> k2)+    -> MonoidMap k1 v+    -> MonoidMap k2 v+mapKeys = mapKeysWith (<>)++-- | \(O(n \log n)\). Applies a function to all the keys of a 'MonoidMap' that+--   are associated with non-'C.null' values, with a combining function for+--   values.+--+-- If the resultant map would contain more than one value for the same key,+-- values are combined together in ascending key order with the given+-- combining function.+--+-- Satisfies the following property:+--+-- @+-- 'mapKeysWith' c f '==' 'fromListWith' c . 'fmap' ('B.first' f) . 'toList'+-- @+--+mapKeysWith+    :: (Ord k2, MonoidNull v)+    => (v -> v -> v)+    -- ^ Function with which to combine values for duplicate keys.+    -> (k1 -> k2)+    -> MonoidMap k1 v+    -> MonoidMap k2 v+mapKeysWith combine fk = fromListWith combine . fmap (B.first fk) . toList++-- | \(O(n)\). Applies a key-dependent function to all non-'C.null' values of+--   a 'MonoidMap'.+--+-- Satisfies the following properties for all functions __@f@__:+--+-- @+-- ('nonNullKey' k m) ==> ('get' k ('mapWithKey' f m) '==' f k ('get' k m))+-- (   'nullKey' k m) ==> ('get' k ('mapWithKey' f m) '==' 'mempty'       )+-- @+--+-- @since 0.0.3.0+--+mapWithKey+    :: MonoidNull v2+    => (k -> v1 -> v2)+    -> MonoidMap k v1+    -> MonoidMap k v2+mapWithKey f (MonoidMap m) =+    MonoidMap . runIdentity $+    Map.traverseMaybeWithKey+        (\k v -> Identity $ maybeNonNull $ applyNonNull (f k) v) m++--------------------------------------------------------------------------------+-- Lazy folding+--------------------------------------------------------------------------------++-- | \(O(n)\). Folds over the values in the map using the given+--   left-associative binary operator.+--+-- Satisfies the following property:+--+-- @+-- 'foldl' f r m '==' 'Map'.'Map.foldl' f r ('toMap' m)+-- @+--+-- @since 0.0.1.7+--+foldl :: (r -> v -> r) -> r -> MonoidMap k v -> r+foldl =+    (coerce+        :: ((r -> v -> r) -> r ->       Map k v -> r)+        -> ((r -> v -> r) -> r -> MonoidMap k v -> r)+    )+    Map.foldl+{-# INLINE foldl #-}++-- | \(O(n)\). Folds over the values in the map using the given+--   right-associative binary operator.+--+-- Satisfies the following property:+--+-- @+-- 'foldr' f r m '==' 'Map'.'Map.foldr' f r ('toMap' m)+-- @+--+-- @since 0.0.1.7+--+foldr :: (v -> r -> r) -> r -> MonoidMap k v -> r+foldr =+    (coerce+        :: ((v -> r -> r) -> r ->       Map k v -> r)+        -> ((v -> r -> r) -> r -> MonoidMap k v -> r)+    )+    Map.foldr+{-# INLINE foldr #-}++-- | \(O(n)\). Folds over the keys and values in the map using the given+--   left-associative binary operator.+--+-- Satisfies the following property:+--+-- @+-- 'foldlWithKey' f r m '==' 'Map'.'Map.foldlWithKey' f r ('toMap' m)+-- @+--+-- @since 0.0.1.7+--+foldlWithKey :: (r -> k -> v -> r) -> r -> MonoidMap k v -> r+foldlWithKey =+    (coerce+        :: ((r -> k -> v -> r) -> r ->       Map k v -> r)+        -> ((r -> k -> v -> r) -> r -> MonoidMap k v -> r)+    )+    Map.foldlWithKey+{-# INLINE foldlWithKey #-}++-- | \(O(n)\). Folds over the keys and values in the map using the given+--   right-associative binary operator.+--+-- Satisfies the following property:+--+-- @+-- 'foldrWithKey' f r m '==' 'Map'.'Map.foldrWithKey' f r ('toMap' m)+-- @+--+-- @since 0.0.1.7+--+foldrWithKey :: (k -> v -> r -> r) -> r -> MonoidMap k v -> r+foldrWithKey =+    (coerce+        :: ((k -> v -> r -> r) -> r ->       Map k v -> r)+        -> ((k -> v -> r -> r) -> r -> MonoidMap k v -> r)+    )+    Map.foldrWithKey+{-# INLINE foldrWithKey #-}++-- | \(O(n)\). Folds over the keys and values in the map using the given+--   monoid.+--+-- Satisfies the following property:+--+-- @+-- 'foldMapWithKey' f m '==' 'Map'.'Map.foldMapWithKey' f ('toMap' m)+-- @+--+-- @since 0.0.1.7+--+foldMapWithKey :: Monoid r => (k -> v -> r) -> MonoidMap k v -> r+foldMapWithKey =+    (coerce+        :: ((k -> v -> r) ->       Map k v -> r)+        -> ((k -> v -> r) -> MonoidMap k v -> r)+    )+    Map.foldMapWithKey+{-# INLINE foldMapWithKey #-}++--------------------------------------------------------------------------------+-- Strict folding+--------------------------------------------------------------------------------++-- | \(O(n)\). A strict version of 'foldl'.+--+-- Each application of the operator is evaluated before using the result in the+-- next application. This function is strict in the starting value.+--+-- @since 0.0.1.7+--+foldl' :: (r -> v -> r) -> r -> MonoidMap k v -> r+foldl' =+    (coerce+        :: ((r -> v -> r) -> r ->       Map k v -> r)+        -> ((r -> v -> r) -> r -> MonoidMap k v -> r)+    )+    Map.foldl'+{-# INLINE foldl' #-}++-- | \(O(n)\). A strict version of 'foldr'.+--+-- Each application of the operator is evaluated before using the result in the+-- next application. This function is strict in the starting value.+--+-- @since 0.0.1.7+--+foldr' :: (v -> r -> r) -> r -> MonoidMap k v -> r+foldr' =+    (coerce+        :: ((v -> r -> r) -> r ->       Map k v -> r)+        -> ((v -> r -> r) -> r -> MonoidMap k v -> r)+    )+    Map.foldr'+{-# INLINE foldr' #-}++-- | \(O(n)\). A strict version of 'foldlWithKey'.+--+-- Each application of the operator is evaluated before using the result in the+-- next application. This function is strict in the starting value.+--+-- @since 0.0.1.7+--+foldlWithKey' :: (r -> k -> v -> r) -> r -> MonoidMap k v -> r+foldlWithKey' =+    (coerce+        :: ((r -> k -> v -> r) -> r ->       Map k v -> r)+        -> ((r -> k -> v -> r) -> r -> MonoidMap k v -> r)+    )+    Map.foldlWithKey'+{-# INLINE foldlWithKey' #-}++-- | \(O(n)\). A strict version of 'foldrWithKey'.+--+-- Each application of the operator is evaluated before using the result in the+-- next application. This function is strict in the starting value.+--+-- @since 0.0.1.7+--+foldrWithKey' :: (k -> v -> r -> r) -> r -> MonoidMap k v -> r+foldrWithKey' =+    (coerce+        :: ((k -> v -> r -> r) -> r ->       Map k v -> r)+        -> ((k -> v -> r -> r) -> r -> MonoidMap k v -> r)+    )+    Map.foldrWithKey'+{-# INLINE foldrWithKey' #-}++-- | \(O(n)\). A strict version of 'foldMapWithKey'.+--+-- Each application of `mappend` is evaluated before using the result in the+-- next application.+--+-- @since 0.0.1.8+--+foldMapWithKey' :: Monoid r => (k -> v -> r) -> MonoidMap k v -> r+foldMapWithKey' f = foldlWithKey' (\r k v -> r <> f k v) mempty+{-# INLINE foldMapWithKey' #-}++--------------------------------------------------------------------------------+-- Traversal+--------------------------------------------------------------------------------++-- | \(O(n)\). Traverses over the values of a map using the given function.+--+-- Satisfies the following property:+--+-- @+-- 'traverse' f m '=='+-- 'fmap' 'fromMap' ('Traversable'.'Traversable.traverse' f ('toMap' m))+-- @+--+-- @since 0.0.1.9+--+traverse+    :: Applicative t+    => MonoidNull v2+    => (v1 -> t v2)+    -> MonoidMap k v1+    -> t (MonoidMap k v2)+traverse f = traverseWithKey (const f)+{-# INLINE traverse #-}++-- | \(O(n)\). Traverses over the keys and values of a map using the given+--   function.+--+-- Satisfies the following property:+--+-- @+-- 'traverseWithKey' f m '=='+-- 'fmap' 'fromMap' ('Map'.'Map.traverseWithKey' f ('toMap' m))+-- @+--+-- @since 0.0.1.9+--+traverseWithKey+    :: Applicative t+    => MonoidNull v2+    => (k -> v1 -> t v2)+    -> MonoidMap k v1+    -> t (MonoidMap k v2)+traverseWithKey f (MonoidMap m) =+    MonoidMap <$>+    Map.traverseMaybeWithKey+        (\k v -> maybeNonNull <$> applyNonNull (f k) v) m+{-# INLINE traverseWithKey #-}++-- | \(O(n)\). Threads an accumulating argument through the map in ascending+--   order of keys.+--+-- Satisfies the following property:+--+-- @+-- 'mapAccumL' f s m '=='+-- 'fmap' 'fromMap' ('Traversable'.'Traversable.mapAccumL' f s ('toMap' m))+-- @+--+-- @since 0.0.1.9+--+mapAccumL+    :: MonoidNull v2+    => (s -> v1 -> (s, v2))+    -> s+    -> MonoidMap k v1+    -> (s, MonoidMap k v2)+mapAccumL f s m =+    (coerce+        :: ((v1 -> StateL s  v2 ) -> MM k v1 -> StateL s (MM k v2))+        -> ((v1 -> s ->  (s, v2)) -> MM k v1 -> s ->  (s, MM k v2))+    )+    traverse (flip f) m s+{-# INLINE mapAccumL #-}++-- | \(O(n)\). Threads an accumulating argument through the map in descending+--   order of keys.+--+-- Satisfies the following property:+--+-- @+-- 'mapAccumR' f s m '=='+-- 'fmap' 'fromMap' ('Traversable'.'Traversable.mapAccumR' f s ('toMap' m))+-- @+--+-- @since 0.0.1.9+--+mapAccumR+    :: MonoidNull v2+    => (s -> v1 -> (s, v2))+    -> s+    -> MonoidMap k v1+    -> (s, MonoidMap k v2)+mapAccumR f s m =+    (coerce+        :: ((v1 -> StateR s  v2 ) -> MM k v1 -> StateR s (MM k v2))+        -> ((v1 -> s ->  (s, v2)) -> MM k v1 -> s ->  (s, MM k v2))+    )+    traverse (flip f) m s+{-# INLINE mapAccumR #-}++-- | \(O(n)\). Threads an accumulating argument through the map in ascending+--   order of keys.+--+-- Satisfies the following property:+--+-- @+-- 'mapAccumLWithKey' f s m '=='+-- 'fmap' 'fromMap' ('Map'.'Map.mapAccumWithKey' f s ('toMap' m))+-- @+--+-- @since 0.0.1.9+--+mapAccumLWithKey+    :: MonoidNull v2+    => (s -> k -> v1 -> (s, v2))+    -> s+    -> MonoidMap k v1+    -> (s, MonoidMap k v2)+mapAccumLWithKey f s0 m =+    (coerce+        :: ((k -> v1 -> StateL s  v2 ) -> MM k v1 -> StateL s (MM k v2))+        -> ((k -> v1 -> s ->  (s, v2)) -> MM k v1 -> s ->  (s, MM k v2))+    )+    traverseWithKey (\k v1 s -> f s k v1) m s0+{-# INLINE mapAccumLWithKey #-}++-- | \(O(n)\). Threads an accumulating argument through the map in descending+--   order of keys.+--+-- Satisfies the following property:+--+-- @+-- 'mapAccumRWithKey' f s m '=='+-- 'fmap' 'fromMap' ('Map'.'Map.mapAccumRWithKey' f s ('toMap' m))+-- @+--+-- @since 0.0.1.9+--+mapAccumRWithKey+    :: MonoidNull v2+    => (s -> k -> v1 -> (s, v2))+    -> s+    -> MonoidMap k v1+    -> (s, MonoidMap k v2)+mapAccumRWithKey f s0 m =+    (coerce+        :: ((k -> v1 -> StateR s  v2 ) -> MM k v1 -> StateR s (MM k v2))+        -> ((k -> v1 -> s ->  (s, v2)) -> MM k v1 -> s ->  (s, MM k v2))+    )+    traverseWithKey (\k v1 s -> f s k v1) m s0+{-# INLINE mapAccumRWithKey #-}++--------------------------------------------------------------------------------+-- Comparison+--------------------------------------------------------------------------------++-- | Indicates whether or not the first map is a /submap/ of the second.+--+-- Map __@m1@__ is a submap of map __@m2@__ if (and only if) __@m1@__ can be+-- subtracted from __@m2@__ with the 'minusMaybe' operation:+--+-- @+-- m1 '`isSubmapOf`' m2 '==' 'isJust' (m2 '`minusMaybe`' m1)+-- @+--+-- Equivalently, map __@m1@__ is a submap of map __@m2@__ if (and only if) for+-- all possible keys __@k@__, the value for __@k@__ in __@m1@__ can be+-- subtracted from the value for __@k@__ in __@m2@__ with the '(</>)' operator:+--+-- @+-- m1 '`isSubmapOf`' m2 '==' (∀ k. 'isJust' ('get' k m2 '</>' 'get' k m1))+-- @+--+isSubmapOf+    :: (Ord k, Monoid v, Reductive v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Bool+isSubmapOf = isSubmapOfBy $ \v1 v2 -> isJust (v2 </> v1)+{-# INLINE isSubmapOf #-}++-- | Indicates whether or not the first map is a /submap/ of the second, using+--   the given function to compare values for matching keys.+--+-- Satisfies the following property:+--+-- @+-- 'isSubmapOfBy' f m1 m2 '=='+--     'all' (\\k -> f ('get' k m1) ('get' k m2)) ('nonNullKeys' m1)+-- @+--+-- === Conditional totality+--+-- /If/ the given comparison function __@f@__ /always/ evaluates to 'True'+-- when its first argument is 'mempty':+--+-- @+-- ∀ v. f 'mempty' v+-- @+--+-- /Then/ the following property holds:+--+-- @+-- 'isSubmapOfBy' f m1 m2 '==' (∀ k. f ('get' k m1) ('get' k m2))+-- @+--+isSubmapOfBy+    :: (Ord k, Monoid v1, Monoid v2)+    => (v1 -> v2 -> Bool)+    -- ^ Function with which to compare values for matching keys.+    -> MonoidMap k v1+    -> MonoidMap k v2+    -> Bool+isSubmapOfBy leq m1 m2 =+    all+        (\k -> get k m1 `leq` get k m2)+        (nonNullKeys m1)+{-# INLINE isSubmapOfBy #-}++-- | Indicates whether or not a pair of maps are /disjoint/.+--+-- Maps __@m1@__ and __@m2@__ are disjoint if (and only if) their intersection+-- is empty:+--+-- @+-- 'disjoint' m1 m2 '==' ('intersection' m1 m2 '==' 'mempty')+-- @+--+-- Equivalently, maps __@m1@__ and __@m2@__ are disjoint if (and only if) for+-- all possible keys __@k@__, the values for __@k@__ in __@m1@__ and __@m2@__+-- have a 'C.gcd' that is 'C.null':+--+-- @+-- 'disjoint' m1 m2 '==' (∀ k. 'C.null' ('C.gcd' ('get' k m1) ('get' k m2)))+-- @+--+disjoint+    :: (Ord k, GCDMonoid v, MonoidNull v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Bool+disjoint = disjointBy (\v1 v2 -> C.null (C.gcd v1 v2))+{-# INLINE disjoint #-}++-- | Indicates whether or not a pair of maps are /disjoint/ using the given+--   indicator function to test pairs of values for matching keys.+--+-- Satisfies the following property:+--+-- @+-- 'disjointBy' f m1 m2 '=='+--     'all'+--         (\\k -> f ('get' k m1) ('get' k m2))+--         ('Set.intersection' ('nonNullKeys' m1) ('nonNullKeys' m2))+-- @+--+-- === Conditional totality+--+-- /If/ the given indicator function __@f@__ /always/ evaluates to 'True'+-- when /either/ or /both/ of its arguments are 'mempty':+--+-- @+-- ∀ v. (f v 'mempty') '&&' (f 'mempty' v)+-- @+--+-- /Then/ the following property holds:+--+-- @+-- 'disjointBy' f m1 m2 '==' (∀ k. f ('get' k m1) ('get' k m2))+-- @+--+disjointBy+    :: (Ord k, Monoid v1, Monoid v2)+    => (v1 -> v2 -> Bool)+    -- ^ Function with which to test pairs of values for matching keys.+    -> MonoidMap k v1+    -> MonoidMap k v2+    -> Bool+disjointBy f m1 m2 =+    all+        (\k -> f (get k m1) (get k m2))+        (Set.intersection (nonNullKeys m1) (nonNullKeys m2))+{-# INLINE disjointBy #-}++--------------------------------------------------------------------------------+-- Association+--------------------------------------------------------------------------------++-- | Appends a pair of maps together.+--+-- Uses the 'Semigroup' operator '(<>)' to append each value in the first map+-- to its matching value in the second map.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k ('append' m1 m2) '==' 'get' k m1 '<>' 'get' k m2+-- @+--+-- This function provides the definition of '(<>)' for the 'MonoidMap' instance+-- of 'Semigroup'.+--+-- === __Examples__+--+-- With 'String' values:+--+-- @+-- >>> m1 = 'fromList' [(1, "abc"), (2, "ij" ), (3, "p"  )            ]+-- >>> m2 = 'fromList' [            (2, "  k"), (3,  "qr"), (4, "xyz")]+-- >>> m3 = 'fromList' [(1, "abc"), (2, "ijk"), (3, "pqr"), (4, "xyz")]+-- @+-- @+-- >>> 'append' m1 m2 '==' m3+-- 'True'+-- @+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural.Natural' values:+--+-- @+-- >>> m1 = 'fromList' [("a", 4), ("b", 2), ("c", 1)          ]+-- >>> m2 = 'fromList' [          ("b", 1), ("c", 2), ("d", 4)]+-- >>> m3 = 'fromList' [("a", 4), ("b", 3), ("c", 3), ("d", 4)]+-- @+-- @+-- >>> 'append' m1 m2 '==' m3+-- 'True'+-- @+--+append+    :: (Ord k, MonoidNull v)+    => MonoidMap k v+    -> MonoidMap k v+    -> MonoidMap k v+append = merge MergeStrategy+    { withNonNullL =+        keepNonNull+        -- Justification:+        --+        -- v <> mempty ≡ v++    , withNonNullR =+        keepNonNull+        -- Justification:+        --+        -- mempty <> v ≡ v++    , withNonNullP =+        withBoth (<>)+    }+{-# INLINE append #-}++--------------------------------------------------------------------------------+-- Prefixes and suffixes+--------------------------------------------------------------------------------++-- | Indicates whether or not the first map is a /prefix/ of the second.+--+-- 'MonoidMap' __@m1@__ is a /prefix/ of 'MonoidMap' __@m2@__ if (and only if)+-- for all possible keys __@k@__, the value for __@k@__ in __@m1@__ is a+-- /prefix/ of the value for __@k@__ in __@m2@__:+--+-- @+-- m1 '`isPrefixOf`' m2 '==' (∀ k. 'get' k m1 '`C.isPrefixOf`' 'get' k m2)+-- @+--+-- This function provides the definition of 'C.isPrefixOf' for the 'MonoidMap'+-- instance of 'LeftReductive'.+--+-- === __Examples__+--+-- With 'String' values:+--+-- @+-- >>> m1 = 'fromList' [(1, "a"  ), (2, "p"  ), (3, "x"  )]+-- >>> m2 = 'fromList' [(1, "abc"), (2, "pqr"), (3, "xyz")]+-- >>> m1 '`isPrefixOf`' m2+-- 'True'+-- @+--+-- @+-- >>> m1 = 'fromList' [            (2, "p"  )            ]+-- >>> m2 = 'fromList' [(1, "abc"), (2, "pqr"), (3, "xyz")]+-- >>> m1 '`isPrefixOf`' m2+-- 'True'+-- @+--+-- @+-- >>> m1 = 'fromList' [(1, "abc"), (2, "p"  ), (3, "x"  )]+-- >>> m2 = 'fromList' [(1, "a"  ), (2, "pqr"), (3, "xyz")]+-- >>> m1 '`isPrefixOf`' m2+-- 'False'+-- @+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural.Natural' values:+--+-- @+-- >>> m1 = 'fromList' [("a", 1), ("b", 1), ("c", 1)]+-- >>> m2 = 'fromList' [("a", 2), ("b", 4), ("c", 8)]+-- >>> m1 '`isPrefixOf`' m2+-- 'True'+-- @+--+-- @+-- >>> m1 = 'fromList' [          ("b", 1)          ]+-- >>> m2 = 'fromList' [("a", 2), ("b", 4), ("c", 8)]+-- >>> m1 '`isPrefixOf`' m2+-- 'True'+-- @+--+-- @+-- >>> m1 = 'fromList' [("a", 2), ("b", 1), ("c", 1)]+-- >>> m2 = 'fromList' [("a", 1), ("b", 4), ("c", 8)]+-- >>> m1 '`isPrefixOf`' m2+-- 'False'+-- @+--+isPrefixOf+    :: (Ord k, Monoid v, LeftReductive v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Bool+isPrefixOf = isSubmapOfBy C.isPrefixOf+    -- Note that in practice, it's sufficient to check the following property:+    --+    -- @+    -- m1 '`isPrefixOf`' m2 '=='+    --     'all'+    --         (\\k -> 'get' k m1 '`C.isPrefixOf`' 'get' k m2)+    --         ('nonNullKeys' m1)+    -- @+    --+    -- ==== Justification+    --+    -- According to the laws for 'LeftReductive':+    --+    -- @+    -- ∀ a b. b '`C.isPrefixOf`' (b '<>' a)+    -- @+    --+    -- Substituting 'mempty' for @b@:+    --+    -- @+    -- ∀ a. 'mempty' '`C.isPrefixOf`' ('mempty' '<>' a)+    -- @+    --+    -- According to the left identity law for 'Monoid':+    --+    -- @+    -- ∀ a. 'mempty' '<>' a '==' a+    -- @+    --+    -- We can therefore assert that:+    --+    -- @+    -- ∀ a. 'mempty' '`C.isPrefixOf`' a+    -- @+    --+    -- Since 'mempty' is /always/ a valid prefix, we only need to consider+    -- values in 'm1' that are /not/ 'mempty'.+    --+    -- The 'nonNullKeys' function, when applied to 'm1', gives us /precisely/+    -- the set of keys that are not associated with 'mempty' in 'm1':+    --+    -- @+    -- (k '`Data.Set.member`' 'nonNullKeys' m1) '==' ('get' k m1 '/=' 'mempty')+    -- @+    --+{-# INLINE isPrefixOf #-}++-- | Indicates whether or not the first map is a /suffix/ of the second.+--+-- 'MonoidMap' __@m1@__ is a /suffix/ of 'MonoidMap' __@m2@__ if (and only if)+-- for all possible keys __@k@__, the value for __@k@__ in __@m1@__ is a+-- /suffix/ of the value for __@k@__ in __@m2@__:+--+-- @+-- m1 '`isSuffixOf`' m2 '==' (∀ k. 'get' k m1 '`C.isSuffixOf`' 'get' k m2)+-- @+--+-- This function provides the definition of 'C.isSuffixOf' for the 'MonoidMap'+-- instance of 'RightReductive'.+--+-- === __Examples__+--+-- With 'String' values:+--+-- @+-- >>> m1 = 'fromList' [(1,   "c"), (2,   "r"), (3,   "z")]+-- >>> m2 = 'fromList' [(1, "abc"), (2, "pqr"), (3, "xyz")]+-- >>> m1 '`isSuffixOf`' m2+-- 'True'+-- @+--+-- @+-- >>> m1 = 'fromList' [            (2,   "r")            ]+-- >>> m2 = 'fromList' [(1, "abc"), (2, "pqr"), (3, "xyz")]+-- >>> m1 '`isSuffixOf`' m2+-- 'True'+-- @+--+-- @+-- >>> m1 = 'fromList' [(1, "abc"), (2,   "r"), (3,   "z")]+-- >>> m2 = 'fromList' [(1,   "c"), (2, "pqr"), (3, "xyz")]+-- >>> m1 '`isSuffixOf`' m2+-- 'False'+-- @+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural.Natural' values:+--+-- @+-- >>> m1 = 'fromList' [("a", 1), ("b", 1), ("c", 1)]+-- >>> m2 = 'fromList' [("a", 2), ("b", 4), ("c", 8)]+-- >>> m1 '`isSuffixOf`' m2+-- 'True'+-- @+--+-- @+-- >>> m1 = 'fromList' [          ("b", 1)          ]+-- >>> m2 = 'fromList' [("a", 2), ("b", 4), ("c", 8)]+-- >>> m1 '`isSuffixOf`' m2+-- 'True'+-- @+--+-- @+-- >>> m1 = 'fromList' [("a", 2), ("b", 1), ("c", 1)]+-- >>> m2 = 'fromList' [("a", 1), ("b", 4), ("c", 8)]+-- >>> m1 '`isSuffixOf`' m2+-- 'False'+-- @+--+isSuffixOf+    :: (Ord k, Monoid v, RightReductive v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Bool+isSuffixOf = isSubmapOfBy C.isSuffixOf+    -- Note that in practice, it's sufficient to check the following property:+    --+    -- @+    -- m1 '`isSuffixOf`' m2 '=='+    --     'all'+    --         (\\k -> 'get' k m1 '`C.isSuffixOf`' 'get' k m2)+    --         ('nonNullKeys' m1)+    -- @+    --+    -- ==== Justification+    --+    -- According to the laws for 'RightReductive':+    --+    -- @+    -- ∀ a b. b '`C.isSuffixOf`' (a '<>' b)+    -- @+    --+    -- Substituting 'mempty' for @b@:+    --+    -- @+    -- ∀ a. 'mempty' '`C.isSuffixOf`' (a '<>' 'mempty')+    -- @+    --+    -- According to the right identity law for 'Monoid':+    --+    -- @+    -- ∀ a. a '<>' 'mempty' '==' a+    -- @+    --+    -- We can therefore assert that:+    --+    -- @+    -- ∀ a. 'mempty' '`C.isSuffixOf`' a+    -- @+    --+    -- Since 'mempty' is /always/ a valid suffix, we only need to consider+    -- values in 'm1' that are /not/ 'mempty'.+    --+    -- The 'nonNullKeys' function, when applied to 'm1', gives us /precisely/+    -- the set of keys that are not associated with 'mempty' in 'm1':+    --+    -- @+    -- (k '`Data.Set.member`' 'nonNullKeys' m1) '==' ('get' k m1 '/=' 'mempty')+    -- @+    --+{-# INLINE isSuffixOf #-}++-- | Strips a /prefix/ from a 'MonoidMap'.+--+-- If map __@m1@__ is a /prefix/ of map __@m2@__, then 'stripPrefix' __@m1@__+-- __@m2@__ will produce a /reduced/ map where prefix __@m1@__ is /stripped/+-- from __@m2@__.+--+-- === Properties+--+-- The 'stripPrefix' function, when applied to maps __@m1@__ and __@m2@__,+-- produces a result if (and only if) __@m1@__ is a prefix of __@m2@__:+--+-- @+-- 'isJust' ('stripPrefix' m1 m2) '==' m1 '`isPrefixOf`' m2+-- @+--+-- The value for any key __@k@__ in the result is /identical/ to the result of+-- stripping the value for __@k@__ in map __@m1@__ from the value for __@k@__+-- in map __@m2@__:+--+-- @+-- 'all'+--    (\\r -> 'Just' ('get' k r) '==' 'C.stripPrefix' ('get' k m1) ('get' k m2))+--    ('stripPrefix' m1 m2)+-- @+--+-- If we append prefix __@m1@__ to the /left-hand/ side of the result, we can+-- always recover the original map __@m2@__:+--+-- @+-- 'all'+--    (\\r -> m1 '<>' r '==' m2)+--    ('stripPrefix' m1 m2)+-- @+--+-- This function provides the definition of 'C.stripPrefix' for the 'MonoidMap'+-- instance of 'LeftReductive'.+--+-- === __Examples__+--+-- With 'String' values:+--+-- @+-- >>> __m1__ = 'fromList' [(1, ""   ), (2, "i"  ), (3, "pq" ), (4, "xyz")]+-- >>> __m2__ = 'fromList' [(1, "abc"), (2, "ijk"), (3, "pqr"), (4, "xyz")]+-- >>> __m3__ = 'fromList' [(1, "abc"), (2,  "jk"), (3,   "r"), (4,    "")]+-- @+-- @+-- >>> 'stripPrefix' __m1__ __m2__ '==' 'Just' __m3__+-- 'True'+-- @+-- @+-- >>> 'stripPrefix' __m2__ __m1__ '==' 'Nothing'+-- 'True'+-- @+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural' values:+--+-- @+-- >>> __m1__ = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3)]+-- >>> __m2__ = 'fromList' [("a", 3), ("b", 3), ("c", 3), ("d", 3)]+-- >>> __m3__ = 'fromList' [("a", 3), ("b", 2), ("c", 1), ("d", 0)]+-- @+-- @+-- >>> 'stripPrefix' __m1__ __m2__ '==' 'Just' __m3__+-- 'True'+-- @+-- @+-- >>> 'stripPrefix' __m2__ __m1__ '==' 'Nothing'+-- 'True'+-- @+--+stripPrefix+    :: (Ord k, MonoidNull v, LeftReductive v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Maybe (MonoidMap k v)+stripPrefix = mergeA MergeStrategy+    { withNonNullL =+        withNonNullA (\v -> C.stripPrefix v mempty)++    , withNonNullR =+        keepNonNull+        -- Justification:+        --+        -- stripPrefix mempty a ≡ a++    , withNonNullP =+        withBothA C.stripPrefix+    }+{-# INLINE stripPrefix #-}++-- | Strips a /suffix/ from a 'MonoidMap'.+--+-- If map __@m1@__ is a /suffix/ of map __@m2@__, then 'stripSuffix' __@m1@__+-- __@m2@__ will produce a /reduced/ map where suffix __@m1@__ is /stripped/+-- from __@m2@__.+--+-- === Properties+--+-- The 'stripSuffix' function, when applied to maps __@m1@__ and __@m2@__,+-- produces a result if (and only if) __@m1@__ is a suffix of __@m2@__:+--+-- @+-- 'isJust' ('stripSuffix' m1 m2) '==' m1 '`isSuffixOf`' m2+-- @+--+-- The value for any key __@k@__ in the result is /identical/ to the result of+-- stripping the value for __@k@__ in map __@m1@__ from the value for __@k@__+-- in map __@m2@__:+--+-- @+-- 'all'+--    (\\r -> 'Just' ('get' k r) '==' 'C.stripSuffix' ('get' k m1) ('get' k m2))+--    ('stripSuffix' m1 m2)+-- @+--+-- If we append suffix __@m1@__ to the /right-hand/ side of the result, we can+-- always recover the original map __@m2@__:+--+-- @+-- 'all'+--    (\\r -> r '<>' m1 '==' m2)+--    ('stripSuffix' m1 m2)+-- @+--+-- This function provides the definition of 'C.stripSuffix' for the 'MonoidMap'+-- instance of 'RightReductive'.+--+-- === __Examples__+--+-- With 'String' values:+--+-- @+-- >>> __m1__ = 'fromList' [(1,    ""), (2,   "k"), (3,  "qr"), (4, "xyz")]+-- >>> __m2__ = 'fromList' [(1, "abc"), (2, "ijk"), (3, "pqr"), (4, "xyz")]+-- >>> __m3__ = 'fromList' [(1, "abc"), (2, "ij" ), (3, "p"  ), (4, ""   )]+-- @+-- @+-- >>> 'stripSuffix' __m1__ __m2__ '==' 'Just' __m3__+-- 'True'+-- @+-- @+-- >>> 'stripSuffix' __m2__ __m1__ '==' 'Nothing'+-- 'True'+-- @+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural' values:+--+-- @+-- >>> __m1__ = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3)]+-- >>> __m2__ = 'fromList' [("a", 3), ("b", 3), ("c", 3), ("d", 3)]+-- >>> __m3__ = 'fromList' [("a", 3), ("b", 2), ("c", 1), ("d", 0)]+-- @+-- @+-- >>> 'stripSuffix' __m1__ __m2__ '==' 'Just' __m3__+-- 'True'+-- @+-- @+-- >>> 'stripSuffix' __m2__ __m1__ '==' 'Nothing'+-- 'True'+-- @+--+stripSuffix+    :: (Ord k, MonoidNull v, RightReductive v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Maybe (MonoidMap k v)+stripSuffix = mergeA MergeStrategy+    { withNonNullL =+        withNonNullA (\v -> C.stripSuffix v mempty)++    , withNonNullR =+        keepNonNull+        -- Justification:+        --+        -- stripSuffix mempty a ≡ a++    , withNonNullP =+        withBothA C.stripSuffix+    }+{-# INLINE stripSuffix #-}++-- | Finds the /greatest common prefix/ of two maps.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k ('commonPrefix' m1 m2)+--     '==' 'C.commonPrefix' ('get' k m1) ('get' k m2)+-- @+--+-- This function provides the definition of 'C.commonPrefix' for the+-- 'MonoidMap' instance of 'LeftGCDMonoid'.+--+-- === __Examples__+--+-- With 'String' values:+--+-- @+-- >>> __m1__ = 'fromList' [(1, "+++"), (2, "b++"), (3, "cc+"), (4, "ddd")]+-- >>> __m2__ = 'fromList' [(1, "---"), (2, "b--"), (3, "cc-"), (4, "ddd")]+-- >>> __m3__ = 'fromList' [(1, ""   ), (2, "b"  ), (3, "cc" ), (4, "ddd")]+-- @+-- @+-- >>> 'commonPrefix' __m1__ __m2__ '==' __m3__+-- 'True'+-- @+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural' values:+--+-- @+-- >>> __m1__ = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3)]+-- >>> __m2__ = 'fromList' [("a", 2), ("b", 2), ("c", 2), ("d", 2)]+-- >>> __m3__ = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 2)]+-- @+-- @+-- >>> 'commonPrefix' __m1__ __m2__ '==' __m3__+-- 'True'+-- @+--+commonPrefix+    :: (Ord k, MonoidNull v, LeftGCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> MonoidMap k v+commonPrefix = merge MergeStrategy+    { withNonNullL =+        keepNull+        -- Justification:+        --+        -- commonPrefix a mempty ≡ mempty++    , withNonNullR =+        keepNull+        -- Justification:+        --+        -- commonPrefix mempty a ≡ mempty++    , withNonNullP =+        withBoth C.commonPrefix+    }+{-# INLINE commonPrefix #-}++-- | Finds the /greatest common suffix/ of two maps.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k ('commonSuffix' m1 m2)+--     '==' 'C.commonSuffix' ('get' k m1) ('get' k m2)+-- @+--+-- This function provides the definition of 'C.commonSuffix' for the+-- 'MonoidMap' instance of 'RightGCDMonoid'.+--+-- === __Examples__+--+-- With 'String' values:+--+-- @+-- >>> __m1__ = 'fromList' [(1, "+++"), (2, "++b"), (3, "+cc"), (4, "ddd")]+-- >>> __m2__ = 'fromList' [(1, "---"), (2, "--b"), (3, "-cc"), (4, "ddd")]+-- >>> __m3__ = 'fromList' [(1,    ""), (2,   "b"), (3,  "cc"), (4, "ddd")]+-- @+-- @+-- >>> 'commonSuffix' __m1__ __m2__ '==' __m3__+-- 'True'+-- @+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural' values:+--+-- @+-- >>> __m1__ = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3)]+-- >>> __m2__ = 'fromList' [("a", 2), ("b", 2), ("c", 2), ("d", 2)]+-- >>> __m3__ = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 2)]+-- @+-- @+-- >>> 'commonSuffix' __m1__ __m2__ '==' __m3__+-- 'True'+-- @+--+commonSuffix+    :: (Ord k, MonoidNull v, RightGCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> MonoidMap k v+commonSuffix = merge MergeStrategy+    { withNonNullL =+        keepNull+        -- Justification:+        --+        -- commonSuffix a mempty ≡ mempty++    , withNonNullR =+        keepNull+        -- Justification:+        --+        -- commonSuffix mempty a ≡ mempty++    , withNonNullP =+        withBoth C.commonSuffix+    }+{-# INLINE commonSuffix #-}++-- | Strips the /greatest common prefix/ from a pair of maps.+--+-- Given two maps __@m1@__ and __@m2@__, 'stripCommonPrefix' produces a+-- tuple __@(p, r1, r2)@__, where:+--+--  - __@p@__ is the /greatest common prefix/ of __@m1@__ and __@m2@__+--  - __@r1@__ is the /remainder/ of stripping prefix __@p@__ from __@m1@__+--  - __@r2@__ is the /remainder/ of stripping prefix __@p@__ from __@m2@__+--+-- The resulting prefix __@p@__ can be appended to the /left-hand/ side of+-- either remainder __@r1@__ or __@r2@__ to /reproduce/ either of the original+-- maps __@m1@__ or __@m2@__ respectively:+--+-- @+-- 'stripCommonPrefix' m1 m2+--    '&' \\(p, r1, _) -> p '<>' r1 '==' m1+-- 'stripCommonPrefix' m1 m2+--    '&' \\(p, _, r2) -> p '<>' r2 '==' m2+-- @+--+-- Prefix __@p@__ is /identical/ to the result of applying 'commonPrefix' to+-- __@m1@__ and __@m2@__:+--+-- @+-- 'stripCommonPrefix' m1 m2+--    '&' \\(p, _, _) -> p '==' 'commonPrefix' m1 m2+-- @+--+-- Remainders __@r1@__ and __@r2@__ are /identical/ to the results of applying+-- 'stripPrefix' to __@p@__ and __@m1@__ or to __@p@__ and __@m2@__+-- respectively:+--+-- @+-- 'stripCommonPrefix' m1 m2+--    '&' \\(p, r1, _) -> 'Just' r1 '==' 'stripPrefix' p m1+-- 'stripCommonPrefix' m1 m2+--    '&' \\(p, _, r2) -> 'Just' r2 '==' 'stripPrefix' p m2+-- @+--+-- This function provides the definition of 'C.stripCommonPrefix' for the+-- 'MonoidMap' instance of 'LeftGCDMonoid'.+--+-- === __Examples__+--+-- With 'String' values:+--+-- @+-- >>> m1 = 'fromList' [(1, "+++"), (2, "a++"), (3, "aa+"), (4, "aaa")]+-- >>> m2 = 'fromList' [(1, "---"), (2, "a--"), (3, "aa-"), (4, "aaa")]+-- @+-- @+-- >>> p  = 'fromList' [(1, ""   ), (2, "a"  ), (3, "aa" ), (4, "aaa")]+-- >>> r1 = 'fromList' [(1, "+++"), (2,  "++"), (3,   "+"), (4,    "")]+-- >>> r2 = 'fromList' [(1, "---"), (2,  "--"), (3,   "-"), (4,    "")]+-- @+-- @+-- >>> 'stripCommonPrefix' m1 m2 '==' (p, r1, r2)+-- 'True'+-- @+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural.Natural' values:+--+-- @+-- >>> m1 = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3), ("e", 4)]+-- >>> m2 = 'fromList' [("a", 4), ("b", 3), ("c", 2), ("d", 1), ("e", 0)]+-- @+-- @+-- >>> p  = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 1), ("e", 0)]+-- >>> r1 = 'fromList' [("a", 0), ("b", 0), ("c", 0), ("d", 2), ("e", 4)]+-- >>> r2 = 'fromList' [("a", 4), ("b", 2), ("c", 0), ("d", 0), ("e", 0)]+-- @+-- @+-- >>> 'stripCommonPrefix' m1 m2 '==' (p, r1, r2)+-- 'True'+-- @+--+stripCommonPrefix+    :: (Ord k, MonoidNull v, LeftGCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> (MonoidMap k v, MonoidMap k v, MonoidMap k v)+stripCommonPrefix = C.stripCommonPrefix++-- | Strips the /greatest common suffix/ from a pair of maps.+--+-- Given two maps __@m1@__ and __@m2@__, 'stripCommonSuffix' produces a+-- tuple __@(r1, r2, s)@__, where:+--+--  - __@s@__ is the /greatest common suffix/ of __@m1@__ and __@m2@__+--  - __@r1@__ is the /remainder/ of stripping suffix __@s@__ from __@m1@__+--  - __@r2@__ is the /remainder/ of stripping suffix __@s@__ from __@m2@__+--+-- The resulting suffix __@s@__ can be appended to the /right-hand/ side of+-- either remainder __@r1@__ or __@r2@__ to /reproduce/ either of the original+-- maps __@m1@__ or __@m2@__ respectively:+--+-- @+-- 'stripCommonSuffix' m1 m2+--    '&' \\(r1, _, s) -> r1 '<>' s '==' m1+-- 'stripCommonSuffix' m1 m2+--    '&' \\(_, r2, s) -> r2 '<>' s '==' m2+-- @+--+-- Suffix __@s@__ is /identical/ to the result of applying 'commonSuffix' to+-- __@m1@__ and __@m2@__:+--+-- @+-- 'stripCommonSuffix' m1 m2+--    '&' \\(_, _, s) -> s '==' 'commonSuffix' m1 m2+-- @+--+-- Remainders __@r1@__ and __@r2@__ are /identical/ to the results of applying+-- 'stripSuffix' to __@s@__ and __@m1@__ or to __@s@__ and __@m2@__+-- respectively:+--+-- @+-- 'stripCommonSuffix' m1 m2+--    '&' \\(r1, _, s) -> 'Just' r1 '==' 'stripSuffix' s m1+-- 'stripCommonSuffix' m1 m2+--    '&' \\(_, r2, s) -> 'Just' r2 '==' 'stripSuffix' s m2+-- @+--+-- This function provides the definition of 'C.stripCommonSuffix' for the+-- 'MonoidMap' instance of 'RightGCDMonoid'.+--+-- === __Examples__+--+-- With 'String' values:+--+-- @+-- >>> m1 = 'fromList' [(1, "+++"), (2, "++a"), (3, "+aa"), (4, "aaa")]+-- >>> m2 = 'fromList' [(1, "---"), (2, "--a"), (3, "-aa"), (4, "aaa")]+-- @+-- @+-- >>> r1 = 'fromList' [(1, "+++"), (2, "++" ), (3, "+"  ), (4, ""   )]+-- >>> r2 = 'fromList' [(1, "---"), (2, "--" ), (3, "-"  ), (4, ""   )]+-- >>> s  = 'fromList' [(1,    ""), (2,   "a"), (3,  "aa"), (4, "aaa")]+-- @+-- @+-- >>> 'stripCommonSuffix' m1 m2 '==' (r1, r2, s)+-- 'True'+-- @+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural.Natural' values:+--+-- @+-- >>> m1 = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3), ("e", 4)]+-- >>> m2 = 'fromList' [("a", 4), ("b", 3), ("c", 2), ("d", 1), ("e", 0)]+-- @+-- @+-- >>> r1 = 'fromList' [("a", 0), ("b", 0), ("c", 0), ("d", 2), ("e", 4)]+-- >>> r2 = 'fromList' [("a", 4), ("b", 2), ("c", 0), ("d", 0), ("e", 0)]+-- >>> s  = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 1), ("e", 0)]+-- @+-- @+-- >>> 'stripCommonSuffix' m1 m2 '==' (r1, r2, s)+-- 'True'+-- @+--+stripCommonSuffix+    :: (Ord k, MonoidNull v, RightGCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> (MonoidMap k v, MonoidMap k v, MonoidMap k v)+stripCommonSuffix = C.stripCommonSuffix++--------------------------------------------------------------------------------+-- Overlap+--------------------------------------------------------------------------------++-- | Finds the /greatest overlap/ of two maps.+--+-- The /greatest overlap/ __@o@__ of maps __@m1@__ and __@m2@__ is the /unique/+-- greatest map that is both a /suffix/ of __@m1@__ and a /prefix/ of __@m2@__:+--+-- @+-- m1 '==' r1 '<>' o \  \+-- m2 '=='    \  \ o '<>' r2+-- @+--+-- Where:+--+--  - __@r1@__ is the /remainder/ obtained by stripping /suffix overlap/+--    __@o@__ from __@m1@__.+--+--      (see 'stripSuffixOverlap')+--+--  - __@r2@__ is the /remainder/ obtained by stripping /prefix overlap/+--    __@o@__ from __@m2@__.+--+--      (see 'stripPrefixOverlap')+--+-- This function satisfies the following property:+--+-- @+-- 'get' k ('overlap' m1 m2) '==' 'C.overlap' ('get' k m1) ('get' k m2)+-- @+--+-- This function provides the definition of 'C.overlap' for the 'MonoidMap'+-- instance of 'OverlappingGCDMonoid'.+--+-- === __Examples__+--+-- With 'String' values:+--+-- @+-- >>> m1 = 'fromList' [(1,"abc"   ), (2,"abcd"  ), (3,"abcde "), (4,"abcdef")]+-- >>> m2 = 'fromList' [(1,   "def"), (2,  "cdef"), (3," bcdef"), (4,"abcdef")]+-- >>> m3 = 'fromList' [(1,   ""   ), (2,  "cd"  ), (3," bcde" ), (4,"abcdef")]+-- @+-- @+-- >>> 'overlap' m1 m2 '==' m3+-- 'True'+-- @+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural' values:+--+-- @+-- >>> m1 = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3), ("e", 4)]+-- >>> m2 = 'fromList' [("a", 4), ("b", 3), ("c", 2), ("d", 1), ("e", 0)]+-- >>> m3 = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 1), ("e", 0)]+-- @+-- @+-- >>> 'overlap' m1 m2 '==' m3+-- 'True'+-- @+--+overlap+    :: (Ord k, MonoidNull v, OverlappingGCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> MonoidMap k v+overlap = merge MergeStrategy+    { withNonNullL =+        keepNull+        -- Justification:+        --+        -- overlap a mempty ≡ mempty++    , withNonNullR =+        keepNull+        -- Justification:+        --+        -- overlap mempty a ≡ mempty++    , withNonNullP =+        withBoth C.overlap+    }+{-# INLINE overlap #-}++-- | /Strips/ from the second map its /greatest prefix overlap/ with suffixes+--   of the first map.+--+-- Evaluating 'stripPrefixOverlap' __@m1@__ __@m2@__ produces the /remainder/+-- __@r2@__:+--+-- @+-- m1 '==' r1 '<>' o \  \+-- m2 '=='    \  \ o '<>' r2+-- @+--+-- Where __@o@__ is the /greatest overlap/ of maps __@m1@__ and __@m2@__: the+-- /unique/ greatest map that is both a /suffix/ of __@m1@__ and a /prefix/ of+-- __@m2@__.+--+-- This function satisfies the following property:+--+-- @+-- 'get' k ('stripPrefixOverlap' m1 m2)+--     '==' 'C.stripPrefixOverlap' ('get' k m1) ('get' k m2)+-- @+--+-- This function provides the definition of 'C.stripPrefixOverlap' for the+-- 'MonoidMap' instance of 'OverlappingGCDMonoid'.+--+-- === __Examples__+--+-- With 'String' values:+--+-- @+-- >>> m1 = 'fromList' [(1,"abc"   ), (2,"abcd"  ), (3,"abcde" ), (4,"abcdef")]+-- >>> m2 = 'fromList' [(1,   "def"), (2,  "cdef"), (3, "bcdef"), (4,"abcdef")]+-- >>> m3 = 'fromList' [(1,   "def"), (2,    "ef"), (3,     "f"), (4,      "")]+-- @+-- @+-- >>> 'stripPrefixOverlap' m1 m2 '==' m3+-- 'True'+-- @+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural' values:+--+-- @+-- >>> m1 = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3), ("e", 4)]+-- >>> m2 = 'fromList' [("a", 4), ("b", 3), ("c", 2), ("d", 1), ("e", 0)]+-- >>> m3 = 'fromList' [("a", 4), ("b", 2), ("c", 0), ("d", 0), ("e", 0)]+-- @+-- @+-- >>> 'stripPrefixOverlap' m1 m2 '==' m3+-- 'True'+-- @+--+stripPrefixOverlap+    :: (Ord k, MonoidNull v, OverlappingGCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> MonoidMap k v+stripPrefixOverlap = merge MergeStrategy+    { withNonNullL =+        keepNull+        -- Justification:+        --+        -- overlap a b      <> stripPrefixOverlap a b      ≡ b+        -- overlap a mempty <> stripPrefixOverlap a mempty ≡ mempty+        --           mempty <> stripPrefixOverlap a mempty ≡ mempty+        --                     stripPrefixOverlap a mempty ≡ mempty++    , withNonNullR =+        keepNonNull+        -- Justification:+        --+        -- overlap a      b <> stripPrefixOverlap a      b ≡ b+        -- overlap mempty b <> stripPrefixOverlap mempty b ≡ b+        --         mempty   <> stripPrefixOverlap mempty b ≡ b+        --                     stripPrefixOverlap mempty b ≡ b++    , withNonNullP =+        withBoth C.stripPrefixOverlap+    }+{-# INLINE stripPrefixOverlap #-}++-- | /Strips/ from the second map its /greatest suffix overlap/ with prefixes+--   of the first map.+--+-- Evaluating 'stripSuffixOverlap' __@m2@__ __@m1@__ produces the /remainder/+-- __@r1@__:+--+-- @+-- m1 '==' r1 '<>' o \  \+-- m2 '=='    \  \ o '<>' r2+-- @+--+-- Where __@o@__ is the /greatest overlap/ of maps __@m1@__ and __@m2@__: the+-- /unique/ greatest map that is both a /suffix/ of __@m1@__ and a /prefix/ of+-- __@m2@__.+--+-- This function satisfies the following property:+--+-- @+-- 'get' k ('stripSuffixOverlap' m2 m1)+--     '==' 'C.stripSuffixOverlap' ('get' k m2) ('get' k m1)+-- @+--+-- This function provides the definition of 'C.stripSuffixOverlap' for the+-- 'MonoidMap' instance of 'OverlappingGCDMonoid'.+--+-- === __Examples__+--+-- With 'String' values:+--+-- @+-- >>> m1 = 'fromList' [(1,"abc"   ), (2,"abcd"  ), (3,"abcde" ), (4,"abcdef")]+-- >>> m2 = 'fromList' [(1,   "def"), (2,  "cdef"), (3, "bcdef"), (4,"abcdef")]+-- >>> m3 = 'fromList' [(1,"abc"   ), (2,"ab"    ), (3,"a"     ), (4,""      )]+-- @+-- @+-- >>> 'stripSuffixOverlap' m2 m1 '==' m3+-- 'True'+-- @+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural' values:+--+-- @+-- >>> m1 = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3), ("e", 4)]+-- >>> m2 = 'fromList' [("a", 4), ("b", 3), ("c", 2), ("d", 1), ("e", 0)]+-- >>> m3 = 'fromList' [("a", 0), ("b", 0), ("c", 0), ("d", 2), ("e", 4)]+-- @+-- @+-- >>> 'stripSuffixOverlap' m2 m1 '==' m3+-- 'True'+-- @+--+stripSuffixOverlap+    :: (Ord k, MonoidNull v, OverlappingGCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> MonoidMap k v+stripSuffixOverlap = merge MergeStrategy+    { withNonNullL =+        keepNull+        -- Justification:+        --+        -- stripSuffixOverlap b a      <> overlap a      b ≡ a+        -- stripSuffixOverlap b mempty <> overlap mempty b ≡ mempty+        -- stripSuffixOverlap b mempty <>         mempty   ≡ mempty+        -- stripSuffixOverlap b mempty                     ≡ mempty++    , withNonNullR =+        keepNonNull+        -- Justification:+        --+        -- stripSuffixOverlap b      a <> overlap a b      ≡ a+        -- stripSuffixOverlap mempty a <> overlap a mempty ≡ a+        -- stripSuffixOverlap mempty a <>           mempty ≡ a+        -- stripSuffixOverlap mempty a                     ≡ a++    , withNonNullP =+        withBoth C.stripSuffixOverlap+    }+{-# INLINE stripSuffixOverlap #-}++-- | Finds the /greatest overlap/ of two maps and /strips/ it from both maps.+--+-- Evaluating 'stripOverlap' __@m1@__ __@m2@__ produces the tuple+-- __@(r1, o, r2)@__, where:+--+-- @+-- m1 '==' r1 '<>' o \  \+-- m2 '=='    \  \ o '<>' r2+-- @+--+-- Where:+--+--  - __@o@__ is the /greatest overlap/ of maps __@m1@__ and __@m2@__: the+--    /unique/ greatest map that is both a /suffix/ of __@m1@__ and a /prefix/+--    of __@m2@__.+--+--      (see 'overlap')+--+--  - __@r1@__ is the /remainder/ obtained by stripping /suffix overlap/+--    __@o@__ from __@m1@__.+--+--      (see 'stripSuffixOverlap')+--+--  - __@r2@__ is the /remainder/ obtained by stripping /prefix overlap/+--    __@o@__ from __@m2@__.+--+--      (see 'stripPrefixOverlap')+--+-- This function satisfies the following property:+--+-- @+-- 'stripOverlap' m1 m2 '=='+--    ( 'stripSuffixOverlap' m2 m1+--    , 'overlap' m1 m2+--    , 'stripPrefixOverlap' m1 m2+--    )+-- @+--+-- This function provides the definition of 'C.stripOverlap' for the+-- 'MonoidMap' instance of 'OverlappingGCDMonoid'.+--+stripOverlap+    :: (Ord k, MonoidNull v, OverlappingGCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> (MonoidMap k v, MonoidMap k v, MonoidMap k v)+stripOverlap m1 m2 =+    ( stripSuffixOverlap m2 m1+    , m1 `overlap` m2+    , stripPrefixOverlap m1 m2+    )++--------------------------------------------------------------------------------+-- Intersection+--------------------------------------------------------------------------------++-- | Finds the /intersection/ of two maps.+--+-- The intersection of maps __@m1@__ and __@m2@__ is the greatest single map+-- __@m@__ that is a /submap/ of both __@m1@__ /and/ __@m2@__:+--+-- @+-- 'intersection' m1 m2 '`isSubmapOf`' m1+-- 'intersection' m1 m2 '`isSubmapOf`' m2+-- @+--+-- The intersection is /unique/:+--+-- @+-- 'and'+--     [ 'intersection' m1 m2 '`isSubmapOf`' m+--     , \            \       \            \ m '`isSubmapOf`' m1+--     , \            \       \            \ m '`isSubmapOf`' m2+--     ]+-- ==>+--     (m '==' 'intersection' m1 m2)+-- @+--+-- The following property holds for all possible keys __@k@__:+--+-- @+-- 'get' k ('intersection' m1 m2) '==' 'C.gcd' ('get' k m1) ('get' k m2)+-- @+--+-- This function provides the definition of 'C.gcd' for the 'MonoidMap'+-- instance of 'GCDMonoid'.+--+-- === __Examples__+--+-- With 'Data.Monoid.Product' 'Numeric.Natural.Natural' values, this function+-- computes the /greatest common divisor/ of each pair of matching values:+--+-- @+-- >>> m1 = 'fromList' [("a", 2), ("b",  6), ("c", 15), ("d", 35)]+-- >>> m2 = 'fromList' [("a", 6), ("b", 15), ("c", 35), ("d", 77)]+-- >>> m3 = 'fromList' [("a", 2), ("b",  3), ("c",  5), ("d",  7)]+-- @+-- @+-- >>> 'intersection' m1 m2 '==' m3+-- 'True'+-- @+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural.Natural' values, this function+-- computes the /minimum/ of each pair of matching values:+--+-- @+-- >>> m1 = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3)]+-- >>> m2 = 'fromList' [("a", 3), ("b", 2), ("c", 1), ("d", 0)]+-- >>> m3 = 'fromList' [("a", 0), ("b", 1), ("c", 1), ("d", 0)]+-- @+-- @+-- >>> 'intersection' m1 m2 '==' m3+-- 'True'+-- @+--+-- With 'Set' 'Numeric.Natural.Natural' values, this function computes the+-- /set/ /intersection/ of each pair of matching values:+--+-- @+-- f xs = 'fromList' ('Set.fromList' '<$>' xs)+-- @+--+-- @+-- >>> m1 = f [("a", [0,1,2]), ("b", [0,1,2  ]), ("c", [0,1,2    ])]+-- >>> m2 = f [("a", [0,1,2]), ("b", [  1,2,3]), ("c", [    2,3,4])]+-- >>> m3 = f [("a", [0,1,2]), ("b", [  1,2  ]), ("c", [    2    ])]+-- @+-- @+-- >>> 'intersection' m1 m2 '==' m3+-- 'True'+-- @+--+intersection+    :: (Ord k, MonoidNull v, GCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> MonoidMap k v+intersection = merge MergeStrategy+    { withNonNullL =+        keepNull+        -- Justification:+        --+        -- gcd a mempty ≡ mempty++    , withNonNullR =+        keepNull+        -- Justification:+        --+        -- gcd mempty b ≡ mempty++    , withNonNullP =+        withBoth C.gcd+    }+{-# INLINE intersection #-}++--------------------------------------------------------------------------------+-- Union+--------------------------------------------------------------------------------++-- | Finds the /union/ of two maps.+--+-- The union of maps __@m1@__ and __@m2@__ is the smallest single map __@m@__+-- that includes both __@m1@__ /and/ __@m2@__ as /submaps/:+--+-- @+-- m1 '`isSubmapOf`' 'union' m1 m2+-- m2 '`isSubmapOf`' 'union' m1 m2+-- @+--+-- The union is /unique/:+--+-- @+-- 'and'+--     [ m1 '`isSubmapOf`' m+--     , m2 '`isSubmapOf`' m+--     ,    \            \ m '`isSubmapOf`' 'union' m1 m2+--     ]+-- ==>+--     (m '==' 'union' m1 m2)+-- @+--+-- The following property holds for all possible keys __@k@__:+--+-- @+-- 'get' k ('union' m1 m2) '==' 'C.lcm' ('get' k m1) ('get' k m2)+-- @+--+-- This function provides the definition of 'C.lcm' for the 'MonoidMap'+-- instance of 'LCMMonoid'.+--+-- === __Examples__+--+-- With 'Data.Monoid.Product' 'Numeric.Natural.Natural' values, this function+-- computes the /least common multiple/ of each pair of matching values:+--+-- @+-- >>> m1 = 'fromList' [("a", 2), ("b",  6), ("c",  15), ("d",  35)]+-- >>> m2 = 'fromList' [("a", 6), ("b", 15), ("c",  35), ("d",  77)]+-- >>> m3 = 'fromList' [("a", 6), ("b", 30), ("c", 105), ("d", 385)]+-- @+-- @+-- >>> 'union' m1 m2 '==' m3+-- 'True'+-- @+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural.Natural' values, this function+-- computes the /maximum/ of each pair of matching values:+--+-- @+-- >>> m1 = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3)]+-- >>> m2 = 'fromList' [("a", 3), ("b", 2), ("c", 1), ("d", 0)]+-- >>> m3 = 'fromList' [("a", 3), ("b", 2), ("c", 2), ("d", 3)]+-- @+-- @+-- >>> 'union' m1 m2 '==' m3+-- 'True'+-- @+--+-- With 'Set' 'Numeric.Natural.Natural' values, this function computes the+-- /set/ /union/ of each pair of matching values:+--+-- @+-- f xs = 'fromList' ('Set.fromList' '<$>' xs)+-- @+--+-- @+-- >>> m1 = f [("a", [0,1,2]), ("b", [0,1,2  ]), ("c", [0,1,2    ])]+-- >>> m2 = f [("a", [0,1,2]), ("b", [  1,2,3]), ("c", [    2,3,4])]+-- >>> m3 = f [("a", [0,1,2]), ("b", [0,1,2,3]), ("c", [0,1,2,3,4])]+-- @+-- @+-- >>> 'union' m1 m2 '==' m3+-- 'True'+-- @+--+union+    :: (Ord k, MonoidNull v, LCMMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> MonoidMap k v+union = merge MergeStrategy+    { withNonNullL =+        keepNonNull+        -- Justification:+        --+        -- lcm a mempty ≡ a++    , withNonNullR =+        keepNonNull+        -- Justification:+        --+        -- lcm mempty a ≡ a++    , withNonNullP =+        withBoth C.lcm+    }+{-# INLINE union #-}++--------------------------------------------------------------------------------+-- Subtraction+--------------------------------------------------------------------------------++-- | Performs /group subtraction/ of the second map from the first.+--+-- Uses the 'Group' subtraction operator '(C.~~)' to subtract each value in the+-- second map from its matching value in the first map.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k (m1 '`minus`' m2) '==' 'get' k m1 'C.~~' 'get' k m2+-- @+--+-- This function provides the definition of '(C.~~)' for the 'MonoidMap'+-- instance of 'Group'.+--+-- === __Examples__+--+-- With 'Data.Monoid.Sum' 'Integer' values, this function performs normal+-- integer subtraction of matching values:+--+-- @+-- >>> m1 = 'fromList' [("a", (-1)), ("b",   0 ), ("c", 1)]+-- >>> m2 = 'fromList' [("a",   1 ), ("b",   1 ), ("c", 1)]+-- >>> m3 = 'fromList' [("a", (-2)), ("b", (-1)), ("c", 0)]+-- @+-- @+-- >>> m1 '`minus`' m2 '==' m3+-- 'True'+-- @+--+-- @+-- >>> m1 = 'fromList' [("a", (-1)), ("b",   0 ), ("c",   1 )]+-- >>> m2 = 'fromList' [("a", (-1)), ("b", (-1)), ("c", (-1))]+-- >>> m3 = 'fromList' [("a",   0 ), ("b",   1 ), ("c",   2 )]+-- @+-- @+-- >>> m1 '`minus`' m2 '==' m3+-- 'True'+-- @+--+minus+    :: (Ord k, MonoidNull v, Group v)+    => MonoidMap k v+    -> MonoidMap k v+    -> MonoidMap k v+minus = merge MergeStrategy+    { withNonNullL =+        keepNonNull+        -- Justification:+        --+        -- a ~~ mempty ≡ a++    , withNonNullR =+        withNonNull C.invert+        -- Justification:+        --+        -- a      ~~ b ≡ a      <> invert b+        -- mempty ~~ b ≡ mempty <> invert b+        -- mempty ~~ b ≡           invert b++    , withNonNullP =+        withBoth (C.~~)+    }+{-# INLINE minus #-}++-- | Performs /reductive subtraction/ of the second map from the first.+--+-- Uses the 'Reductive' subtraction operator '(</>)' to subtract each value in+-- the second map from its matching value in the first map.+--+-- This function produces a result if (and only if) for all possible keys+-- __@k@__, it is possible to subtract the value for __@k@__ in the second map+-- from the value for __@k@__ in the first map:+--+-- @+-- 'isJust' (m1 '`minusMaybe`' m2)+--     '==' (∀ k. 'isJust' ('get' k m1 '</>' 'get' k m2))+-- @+--+-- Otherwise, this function returns 'Nothing'.+--+-- This function satisfies the following property:+--+-- @+-- 'all'+--    (\\r -> 'Just' ('get' k r) '==' 'get' k m1 '</>' 'get' k m2)+--    (m1 '`minusMaybe`' m2)+-- @+--+-- This function provides the definition of '(</>)' for the 'MonoidMap'+-- instance of 'Reductive'.+--+-- === __Examples__+--+-- With 'Set' 'Numeric.Natural.Natural' values, this function performs /set/+-- /subtraction/ of matching values, succeeding if (and only if) each value+-- from the second map is a subset of its matching value from the first map:+--+-- @+-- f xs = 'fromList' ('Set.fromList' '<$>' xs)+-- @+--+-- @+-- >>> m1 = f [("a", [0,1,2]), ("b", [0,1,2])]+-- >>> m2 = f [("a", [     ]), ("b", [0,1,2])]+-- >>> m3 = f [("a", [0,1,2]), ("b", [     ])]+-- @+-- @+-- >>> m1 '`minusMaybe`' m2 '==' 'Just' m3+-- 'True'+-- @+--+-- @+-- >>> m1 = f [("a", [0,1,2]), ("b", [0,1,2]), ("c", [0,1,2])]+-- >>> m2 = f [("a", [0    ]), ("b", [  1  ]), ("c", [    2])]+-- >>> m3 = f [("a", [  1,2]), ("b", [0,  2]), ("c", [0,1  ])]+-- @+-- @+-- >>> m1 '`minusMaybe`' m2 '==' 'Just' m3+-- 'True'+-- @+--+-- @+-- >>> m1 = f [("a", [0,1,2    ]), ("b", [0,1,2    ]), ("c", [0,1,2    ])]+-- >>> m2 = f [("a", [    2,3,4]), ("b", [  1,2,3,4]), ("c", [0,1,2,3,4])]+-- @+-- @+-- >>> m1 '`minusMaybe`' m2 '==' 'Nothing'+-- 'True'+-- @+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural.Natural' values, this function+-- performs /ordinary/ /subtraction/ of matching values, succeeding if (and only+-- if) each value from the second map is less than or equal to its matching+-- value from the first map:+--+-- @+-- >>> m1 = 'fromList' [("a", 2), ("b", 3), ("c", 5), ("d", 8)]+-- >>> m2 = 'fromList' [("a", 0), ("b", 0), ("c", 0), ("d", 0)]+-- >>> m3 = 'fromList' [("a", 2), ("b", 3), ("c", 5), ("d", 8)]+-- @+-- @+-- >>> m1 '`minusMaybe`' m2 '==' 'Just' m3+-- 'True'+-- @+--+-- @+-- >>> m1 = 'fromList' [("a", 2), ("b", 3), ("c", 5), ("d", 8)]+-- >>> m2 = 'fromList' [("a", 1), ("b", 2), ("c", 3), ("d", 5)]+-- >>> m3 = 'fromList' [("a", 1), ("b", 1), ("c", 2), ("d", 3)]+-- @+-- @+-- >>> m1 '`minusMaybe`' m2 '==' 'Just' m3+-- 'True'+-- @+--+-- @+-- >>> m1 = 'fromList' [("a", 2), ("b", 3), ("c", 5), ("d", 8)]+-- >>> m2 = 'fromList' [("a", 2), ("b", 3), ("c", 5), ("d", 8)]+-- >>> m3 = 'fromList' [("a", 0), ("b", 0), ("c", 0), ("d", 0)]+-- @+-- @+-- >>> m1 '`minusMaybe`' m2 '==' 'Just' m3+-- 'True'+-- @+--+-- @+-- >>> m1 = 'fromList' [("a", 2), ("b", 3), ("c", 5), ("d", 8)]+-- >>> m2 = 'fromList' [("a", 3), ("b", 3), ("c", 5), ("d", 8)]+-- @+-- @+-- >>> m1 '`minusMaybe`' m2 '==' 'Nothing'+-- 'True'+-- @+--+minusMaybe+    :: (Ord k, MonoidNull v, Reductive v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Maybe (MonoidMap k v)+minusMaybe = mergeA MergeStrategy+    { withNonNullL =+        keepNonNull+        -- Justification:+        --+        -- According to laws for Reductive:+        -- maybe a (b      <>) (a </> b     ) ≡       a+        -- maybe a (mempty <>) (a </> mempty) ≡       a+        -- maybe a (id       ) (a </> mempty) ≡       a+        --                     (a </> mempty) ∈ {Just a, Nothing}+        --+        -- According to laws for LeftReductive and RightReductive:+        -- isJust (a </> b     ) ≡ b      `isPrefixOf` a ≡ b      `isSuffixOf` a+        -- isJust (a </> mempty) ≡ mempty `isPrefixOf` a ≡ mempty `isSuffixOf` a+        --+        -- According to laws for LeftReductive and RightReductive:+        -- b      `isPrefixOf` (b      <> a)+        -- mempty `isPrefixOf` (mempty <> a)+        -- mempty `isPrefixOf`            a+        --+        -- Therefore:+        -- a </> mempty ≡ Just a++    , withNonNullR =+        withNonNullA (\v -> mempty </> v)++    , withNonNullP =+        withBothA (</>)+    }+{-# INLINE minusMaybe #-}++-- | Performs /monus subtraction/ of the second map from the first.+--+-- Uses the 'Monus' subtraction operator '(<\>)' to subtract each value in+-- the second map from its matching value in the first map.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k (m1 '`monus`' m2) '==' 'get' k m1 '<\>' 'get' k m2+-- @+--+-- This function provides the definition of '(<\>)' for the 'MonoidMap'+-- instance of 'Monus'.+--+-- === __Examples__+--+-- With 'Set' 'Numeric.Natural.Natural' values, this function performs /set/+-- /subtraction/ of matching values:+--+-- @+-- f xs = 'fromList' ('Set.fromList' '<$>' xs)+-- @+--+-- @+-- >>> m1 = f [("a", [0,1,2]), ("b", [0,1,2])]+-- >>> m2 = f [("a", [     ]), ("b", [0,1,2])]+-- >>> m3 = f [("a", [0,1,2]), ("b", [     ])]+-- @+-- @+-- >>> m1 '`monus`' m2 '==' m3+-- 'True'+-- @+--+-- @+-- >>> m1 = f [("a", [0,1,2]), ("b", [0,1,2]), ("c", [0,1,2])]+-- >>> m2 = f [("a", [0    ]), ("b", [  1  ]), ("c", [    2])]+-- >>> m3 = f [("a", [  1,2]), ("b", [0,  2]), ("c", [0,1  ])]+-- @+-- @+-- >>> m1 '`monus`' m2 '==' m3+-- 'True'+-- @+--+-- @+-- >>> m1 = f [("a", [0,1,2    ]), ("b", [0,1,2    ]), ("c", [0,1,2    ])]+-- >>> m2 = f [("a", [    2,3,4]), ("b", [  1,2,3,4]), ("c", [0,1,2,3,4])]+-- >>> m3 = f [("a", [0,1      ]), ("b", [0        ]), ("c", [         ])]+-- @+-- @+-- >>> m1 '`monus`' m2 '==' m3+-- 'True'+-- @+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural.Natural' values, this function+-- performs /truncated/ /subtraction/ of matching values:+--+-- @+-- >>> m1 = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3)]+-- >>> m2 = 'fromList' [("a", 0), ("b", 0), ("c", 0), ("d", 0)]+-- >>> m3 = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3)]+-- @+-- @+-- >>> m1 '`monus`' m2 '==' m3+-- 'True'+-- @+--+-- @+-- >>> m1 = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3)]+-- >>> m2 = 'fromList' [("a", 1), ("b", 1), ("c", 1), ("d", 1)]+-- >>> m3 = 'fromList' [("a", 0), ("b", 0), ("c", 1), ("d", 2)]+-- @+-- @+-- >>> m1 '`monus`' m2 '==' m3+-- 'True'+-- @+--+-- @+-- >>> m1 = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3)]+-- >>> m2 = 'fromList' [("a", 2), ("b", 2), ("c", 2), ("d", 2)]+-- >>> m3 = 'fromList' [("a", 0), ("b", 0), ("c", 0), ("d", 1)]+-- @+-- @+-- >>> m1 '`monus`' m2 '==' m3+-- 'True'+-- @+--+-- @+-- >>> m1 = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3)]+-- >>> m2 = 'fromList' [("a", 4), ("b", 4), ("c", 4), ("d", 4)]+-- >>> m3 = 'fromList' [("a", 0), ("b", 0), ("c", 0), ("d", 0)]+-- @+-- @+-- >>> m1 '`monus`' m2 '==' m3+-- 'True'+-- @+--+monus+    :: (Ord k, MonoidNull v, Monus v)+    => MonoidMap k v+    -> MonoidMap k v+    -> MonoidMap k v+monus = merge MergeStrategy+    { withNonNullL =+        keepNonNull+        -- Justification:+        --+        -- a      <> (b <\> a     ) ≡ b <> (a      <\> b)+        -- mempty <> (b <\> mempty) ≡ b <> (mempty <\> a)+        --            b <\> mempty  ≡ b <> (mempty <\> a)+        --            b <\> mempty  ≡ b <>  mempty+        --            b <\> mempty  ≡ b++    , withNonNullR =+        keepNull+        -- Justification:+        --+        -- mempty <\> a ≡ mempty++    , withNonNullP =+        withBoth (<\>)+    }+{-# INLINE monus #-}++--------------------------------------------------------------------------------+-- Inversion+--------------------------------------------------------------------------------++-- | Inverts every value in a map.+--+-- Applies the 'Group' method 'C.invert' to every value in a map.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k ('invert' m) '==' 'C.invert' ('get' k m)+-- @+--+-- This function provides the definition of 'C.invert' for the 'MonoidMap'+-- instance of 'Group'.+--+-- === __Examples__+--+-- With 'Data.Monoid.Sum' 'Integer' values, this function performs negation+-- of values:+--+-- @+-- >>> m1 = 'fromList' [("a", (-1)), ("b", 0), ("c",   1) ]+-- >>> m2 = 'fromList' [("a",   1 ), ("b", 0), ("c", (-1))]+-- @+-- @+-- >>> 'negate' m1 '==' m2+-- 'True'+-- @+--+invert+    :: (MonoidNull v, Group v)+    => MonoidMap k v+    -> MonoidMap k v+invert = map C.invert+{-# INLINE invert #-}++--------------------------------------------------------------------------------+-- Exponentiation+--------------------------------------------------------------------------------++-- | Performs exponentiation of every value in a map.+--+-- Uses the 'Group' exponentiation method 'C.pow' to raise every value in a map+-- to the power of the given exponent.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k (m '`power`' i) '==' 'get' k m '`C.pow`' i+-- @+--+-- This function provides the definition of 'C.pow' for the 'MonoidMap'+-- instance of 'Group'.+--+-- === __Examples__+--+-- With 'Data.Monoid.Sum' 'Numeric.Natural.Natural' values, this function+-- performs /ordinary multiplication/ of all values by the given exponent:+--+-- @+-- >>> m1 = 'fromList' [("a", 0), ("b", 1), ("c", 2), ("d", 3)]+-- >>> m2 = 'fromList' [("a", 0), ("b", 2), ("c", 4), ("d", 6)]+-- @+-- @+-- >>> m1 '`power`' 2 '==' m2+-- 'True'+-- @+--+-- @+-- >>> m1 = 'fromList' [("a", 0), ("b",   1 ), ("c",   2 ), ("d",   3 )]+-- >>> m2 = 'fromList' [("a", 0), ("b", (-1)), ("c", (-2)), ("d", (-3))]+-- @+-- @+-- >>> m1 '`power`' (-1) '==' m2+-- 'True'+-- @+--+power+    :: (Integral i, MonoidNull v, Group v)+    => MonoidMap k v+    -> i+    -> MonoidMap k v+power m i = map (`C.pow` i) m+{-# INLINE power #-}++--------------------------------------------------------------------------------+-- Intersection+--------------------------------------------------------------------------------++-- | Computes the /intersection/ of a pair of maps using the given function+--   to combine values for matching keys.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k ('intersectionWith' f m1 m2) '=='+--     if k '`Set.member`'+--         'Set.intersection'+--             ('nonNullKeys' m1)+--             ('nonNullKeys' m2)+--     then f ('get' k m1) ('get' k m2)+--     else 'mempty'+-- @+--+-- === Conditional totality+--+-- /If/ the given combining function __@f@__ /always/ produces 'mempty' when+-- /either/ or /both/ of its arguments are 'mempty':+--+-- @+-- (f v      'mempty' '==' 'mempty') '&&'+-- (f 'mempty' v      '==' 'mempty')+-- @+--+-- /Then/ the following property holds for all possible keys __@k@__:+--+-- @+-- 'get' k ('intersectionWith' f m1 m2) '==' f ('get' k m1) ('get' k m2)+-- @+--+-- === __Examples__+--+-- With the 'Prelude.min' function applied to 'Data.Monoid.Sum'+-- 'Numeric.Natural.Natural' values:+--+-- @+-- >>> m1 = 'fromList' [("a", 4), ("b", 3), ("c", 2), ("d", 1)          ]+-- >>> m2 = 'fromList' [          ("b", 1), ("c", 2), ("d", 3), ("e", 4)]+-- >>> m3 = 'fromList' [          ("b", 1), ("c", 2), ("d", 1)          ]+-- @+-- @+-- >>> 'intersectionWith' 'Prelude.min' m1 m2 '==' m3+-- 'True'+-- @+--+intersectionWith+    :: (Ord k, MonoidNull v3)+    => (v1 -> v2 -> v3)+    -- ^ Function with which to combine values for matching keys.+    -> MonoidMap k v1+    -> MonoidMap k v2+    -> MonoidMap k v3+intersectionWith f = merge MergeStrategy+    { withNonNullL =+        keepNull+    , withNonNullR =+        keepNull+    , withNonNullP =+        withBoth f+    }+{-# INLINE intersectionWith #-}++-- | An /applicative/ version of 'intersectionWith'.+--+-- Satisfies the following property:+--+-- @+-- 'runIdentity' ('intersectionWithA' (('fmap' . 'fmap') 'Identity' f) m1 m2)+--          '==' ('intersectionWith'    \    \   \    \  \        \ f  m1 m2)+-- @+--+intersectionWithA+    :: (Applicative f, Ord k, MonoidNull v3)+    => (v1 -> v2 -> f v3)+    -- ^ Function with which to combine values for matching keys.+    -> MonoidMap k v1+    -> MonoidMap k v2+    -> f (MonoidMap k v3)+intersectionWithA f = mergeA MergeStrategy+    { withNonNullL =+        keepNull+    , withNonNullR =+        keepNull+    , withNonNullP =+        withBothA f+    }+{-# INLINE intersectionWithA #-}++--------------------------------------------------------------------------------+-- Union+--------------------------------------------------------------------------------++-- | Computes the /union/ of a pair of maps using the given function to combine+--   values for matching keys.+--+-- Satisfies the following property for all possible keys __@k@__:+--+-- @+-- 'get' k ('unionWith' f m1 m2) '=='+--     if k '`Set.member`'+--         'Set.union'+--             ('nonNullKeys' m1)+--             ('nonNullKeys' m2)+--     then f ('get' k m1) ('get' k m2)+--     else 'mempty'+-- @+--+-- === Conditional totality+--+-- /If/ the given combining function __@f@__ /always/ produces 'mempty' when+-- /both/ of its arguments are 'mempty':+--+-- @+-- f 'mempty' 'mempty' '==' 'mempty'+-- @+--+-- /Then/ the following property holds for all possible keys __@k@__:+--+-- @+-- 'get' k ('unionWith' f m1 m2) '==' f ('get' k m1) ('get' k m2)+-- @+--+-- === __Examples__+--+-- With the 'Prelude.max' function applied to 'Data.Monoid.Sum'+-- 'Numeric.Natural.Natural' values:+--+-- @+-- >>> m1 = 'fromList' [("a", 4), ("b", 3), ("c", 2), ("d", 1)          ]+-- >>> m2 = 'fromList' [          ("b", 1), ("c", 2), ("d", 3), ("e", 4)]+-- >>> m3 = 'fromList' [("a", 4), ("b", 3), ("c", 2), ("d", 3), ("e", 4)]+-- @+-- @+-- >>> 'unionWith' 'Prelude.max' m1 m2 '==' m3+-- 'True'+-- @+--+unionWith+    :: (Ord k, Monoid v1, Monoid v2, MonoidNull v3)+    => (v1 -> v2 -> v3)+    -- ^ Function with which to combine values for matching keys.+    -> MonoidMap k v1+    -> MonoidMap k v2+    -> MonoidMap k v3+unionWith f = merge MergeStrategy+    { withNonNullL =+        withNonNull (\v -> f v mempty)+    , withNonNullR =+        withNonNull (\v -> f mempty v)+    , withNonNullP =+        withBoth f+    }+{-# INLINE unionWith #-}++-- | An /applicative/ version of 'unionWith'.+--+-- Satisfies the following property:+--+-- @+-- 'runIdentity' ('unionWithA' (('fmap' . 'fmap') 'Identity' f) m1 m2)+--          '==' ('unionWith'    \    \   \    \  \        \ f  m1 m2)+-- @+--+unionWithA+    :: (Applicative f, Ord k, Monoid v1, Monoid v2, MonoidNull v3)+    => (v1 -> v2 -> f v3)+    -- ^ Function with which to combine values for matching keys.+    -> MonoidMap k v1+    -> MonoidMap k v2+    -> f (MonoidMap k v3)+unionWithA f = mergeA MergeStrategy+    { withNonNullL =+        withNonNullA (\v -> f v mempty)+    , withNonNullR =+        withNonNullA (\v -> f mempty v)+    , withNonNullP =+        withBothA f+    }+{-# INLINE unionWithA #-}++--------------------------------------------------------------------------------+-- Merging+--------------------------------------------------------------------------------++type WhenOneSideNull f k          vx                        vr+   = Map.WhenMissing f k (NonNull vx)              (NonNull vr)+type WhenBothNonNull f k          v1           v2           vr+   = Map.WhenMatched f k (NonNull v1) (NonNull v2) (NonNull vr)++data MergeStrategy f k v1 v2 v3 = MergeStrategy+    { withNonNullL :: !(WhenOneSideNull f k v1    v3)+    , withNonNullR :: !(WhenOneSideNull f k    v2 v3)+    , withNonNullP :: !(WhenBothNonNull f k v1 v2 v3)+    }++merge+    :: Ord k+    => MergeStrategy Identity k v1 v2 v3+    -> MonoidMap k v1+    -> MonoidMap k v2+    -> MonoidMap k v3+merge (MergeStrategy nnl nnr nnp) (MonoidMap m1) (MonoidMap m2) =+    MonoidMap $ Map.merge nnl nnr nnp m1 m2+{-# INLINE merge #-}++mergeA+    :: (Applicative f, Ord k)+    => MergeStrategy f k v1 v2 v3+    -> MonoidMap k v1+    -> MonoidMap k v2+    -> f (MonoidMap k v3)+mergeA (MergeStrategy nnl nnr nnp) (MonoidMap m1) (MonoidMap m2) =+    MonoidMap <$> Map.mergeA nnl nnr nnp m1 m2+{-# INLINE mergeA #-}++keepNull+    :: Applicative f+    => WhenOneSideNull f k v1 v2+keepNull = Map.dropMissing+{-# INLINE keepNull #-}++keepNonNull+    :: Applicative f+    => WhenOneSideNull f k v v+keepNonNull = Map.preserveMissing+{-# INLINE keepNonNull #-}++withNonNull+    :: (Applicative f, MonoidNull v2)+    => (v1 -> v2)+    -> WhenOneSideNull f k v1 v2+withNonNull f+    = Map.mapMaybeMissing+    $ \_k v -> maybeNonNull $ applyNonNull f v+{-# INLINE withNonNull #-}++withNonNullA+    :: (Applicative f, MonoidNull v2)+    => (v1 -> f v2)+    -> WhenOneSideNull f k v1 v2+withNonNullA f+    = Map.traverseMaybeMissing+    $ \_k v -> maybeNonNull <$> applyNonNull f v+{-# INLINE withNonNullA #-}++withBoth+    :: (Applicative f, MonoidNull v3)+    => (v1 -> v2 -> v3)+    -> WhenBothNonNull f k v1 v2 v3+withBoth f+    = Map.zipWithMaybeMatched+    $ \_k v1 v2 -> maybeNonNull $ applyNonNull2 f v1 v2+{-# INLINE withBoth #-}++withBothA+    :: (Applicative f, MonoidNull v3)+    => (v1 -> v2 -> f v3)+    -> WhenBothNonNull f k v1 v2 v3+withBothA f+    = Map.zipWithMaybeAMatched+    $ \_k v1 v2 -> maybeNonNull <$> applyNonNull2 f v1 v2+{-# INLINE withBothA #-}++--------------------------------------------------------------------------------+-- State+--------------------------------------------------------------------------------++newtype StateL s a = StateL (s -> (s, a))+newtype StateR s a = StateR (s -> (s, a))++instance Functor (StateL s) where+    fmap f (StateL kx) =+        StateL $ \s -> let (s', x) = kx s in (s', f x)++instance Functor (StateR s) where+    fmap f (StateR kx) =+        StateR $ \s -> let (s', x) = kx s in (s', f x)++instance Applicative (StateL s) where+    pure a = StateL $+        \s -> (s, a)+    StateL kf <*> StateL kx = StateL $+        \s ->+            let (s' , f  ) = kf s+                (s'',   x) = kx s'+            in  (s'', f x)+    liftA2 f (StateL kx) (StateL ky) = StateL $+        \s ->+            let (s' ,   x  ) = kx s+                (s'',     y) = ky s'+            in  (s'', f x y)++instance Applicative (StateR s) where+    pure a = StateR $+        \s -> (s, a)+    StateR kf <*> StateR kx = StateR $+        \s ->+            let (s',    x) = kx s+                (s'', f  ) = kf s'+            in  (s'', f x)+    liftA2 f (StateR kx) (StateR ky) = StateR $+        \s ->+            let (s' ,     y) = ky s+                (s'',   x  ) = kx s'+            in  (s'', f x y)
+ components/monoidmap-internal/Data/MonoidMap/Internal/RecoveredMap.hs view
@@ -0,0 +1,125 @@+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+-- An ordinary left-biased map similar to 'Map', implemented in terms of+-- 'MonoidMap'.+--+module Data.MonoidMap.Internal.RecoveredMap+    ( Map+    , empty+    , singleton+    , fromList+    , toList+    , delete+    , insert+    , keysSet+    , lookup+    , member+    , map+    , mapWithKey+    , mapAccumL+    , mapAccumLWithKey+    , mapAccumR+    , mapAccumRWithKey+    )+    where++import Prelude hiding+    ( lookup, map )++import Control.DeepSeq+    ( NFData )+import Data.Coerce+    ( coerce )+import Data.Maybe+    ( mapMaybe )+import Data.Monoid+    ( First (..) )+import Data.MonoidMap.Internal+    ( MonoidMap )+import Data.Semigroup+    ( Semigroup (stimes), stimesIdempotentMonoid )+import Data.Set+    ( Set )++import qualified Data.MonoidMap.Internal as MonoidMap++newtype Map k v = Map+    --  'First' is used to mimic the left-biased nature of 'Data.Map':+    {unMap :: MonoidMap k (First v)}+    deriving newtype (Eq, NFData, Monoid)++instance Ord k => Semigroup (Map k v) where+    (<>) = coerce @(MonoidMap k (First v) -> _ -> _) (<>)+    stimes = stimesIdempotentMonoid++instance (Show k, Show v) => Show (Map k v) where+    show = ("fromList " <>) . show . toList++instance Functor (Map k) where+    fmap = map++empty :: Map k v+empty = Map MonoidMap.empty++singleton :: Ord k => k -> v -> Map k v+singleton k = Map . MonoidMap.singleton k . pure++fromList :: Ord k => [(k, v)] -> Map k v+fromList = Map . MonoidMap.fromListWith (const id) . fmap (fmap pure)++toList :: Map k v -> [(k, v)]+toList = mapMaybe (getFirst . sequenceA) . MonoidMap.toList . unMap++delete :: Ord k => k -> Map k v -> Map k v+delete k = Map . MonoidMap.nullify k . unMap++insert :: Ord k => k -> v -> Map k v -> Map k v+insert k v = Map . MonoidMap.set k (pure v) . unMap++keysSet :: Map k v -> Set k+keysSet = MonoidMap.nonNullKeys . unMap++lookup :: Ord k => k -> Map k v -> Maybe v+lookup k = getFirst . MonoidMap.get k . unMap++member :: Ord k => k -> Map k v -> Bool+member k = MonoidMap.nonNullKey k . unMap++map :: (v1 -> v2) -> Map k v1 -> Map k v2+map f = Map . MonoidMap.map (fmap f) . unMap++mapWithKey :: (k -> v1 -> v2) -> Map k v1 -> Map k v2+mapWithKey f = Map . MonoidMap.mapWithKey (fmap . f) . unMap++mapAccumL :: (s -> v1 -> (s, v2)) -> s -> Map k v1 -> (s, Map k v2)+mapAccumL f s m = Map <$> MonoidMap.mapAccumL (accum f) s (unMap m)++mapAccumR :: (s -> v1 -> (s, v2)) -> s -> Map k v1 -> (s, Map k v2)+mapAccumR f s m = Map <$> MonoidMap.mapAccumR (accum f) s (unMap m)++mapAccumLWithKey :: (s -> k -> v1 -> (s, v2)) -> s -> Map k v1 -> (s, Map k v2)+mapAccumLWithKey f s m =+    Map <$> MonoidMap.mapAccumLWithKey (accumWithKey f) s (unMap m)++mapAccumRWithKey :: (s -> k -> v1 -> (s, v2)) -> s -> Map k v1 -> (s, Map k v2)+mapAccumRWithKey f s m =+    Map <$> MonoidMap.mapAccumRWithKey (accumWithKey f) s (unMap m)++--------------------------------------------------------------------------------+-- Utilities+--------------------------------------------------------------------------------++accum :: (s -> v1 -> (s, v2)) -> s -> First v1 -> (s, First v2)+accum f s1 (First mv1) = case mv1 of+    Just v1 -> let (s2, v2) = f s1 v1 in (s2, First (Just v2))+    Nothing -> (s1, First Nothing)++accumWithKey :: (s -> k -> v1 -> (s, v2)) -> s -> k -> First v1 -> (s, First v2)+accumWithKey f s1 k (First mv1) = case mv1 of+    Just v1 -> let (s2, v2) = f s1 k v1 in (s2, First (Just v2))+    Nothing -> (s1, First Nothing)
+ components/monoidmap-internal/Data/MonoidMap/Internal/Unsafe.hs view
@@ -0,0 +1,50 @@+{-# OPTIONS_GHC -fno-warn-unused-imports #-}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+-- Provides /unsafe/ operations for the 'MonoidMap' type.+--+module Data.MonoidMap.Internal.Unsafe+    (+    -- * Construction+      unsafeFromMap+    )+    where++import Prelude++import Data.Coerce+    ( coerce )+import Data.Map.Strict+    ( Map )+import Data.MonoidMap.Internal+    ( MonoidMap (..), NonNull (..), fromMap )++import qualified Data.Foldable as F+import qualified Data.Monoid.Null as Null+import qualified Data.MonoidMap.Internal as Internal++--------------------------------------------------------------------------------+-- Unsafe construction+--------------------------------------------------------------------------------++-- | \(O(1)\). /Unsafely/ constructs a 'MonoidMap' from an ordinary 'Map'.+--+-- Constructs a 'MonoidMap' in /constant time/, without imposing the burden+-- of a canonicalisation step to remove 'null' values.+--+-- When applied to a given 'Map' @m@, this function /expects/ but does /not/+-- check the following pre-condition:+--+-- @+-- 'F.all' ('not' . 'Null.null') m+-- @+--+-- Not satisfying this pre-condition will result in undefined behaviour.+--+-- See 'fromMap' for a safe version of this function.+--+unsafeFromMap :: Map k v -> MonoidMap k v+unsafeFromMap = coerce
+ components/monoidmap-test/Data/MonoidMap/Internal/AccessSpec.hs view
@@ -0,0 +1,172 @@+{- HLINT ignore "Redundant bracket" -}+{- HLINT ignore "Use camelCase" -}+{- HLINT ignore "Use null" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.AccessSpec+    ( spec+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Function+    ( (&) )+import Data.MonoidMap.Internal+    ( MonoidMap )+import Data.Proxy+    ( Proxy (..) )+import Test.Common+    ( Key+    , Test+    , TestValueType (TestValueType)+    , makeSpec+    , property+    , testValueTypesAll+    )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Fun, Property, applyFun, cover, (===) )++import qualified Data.Monoid.Null as Null+import qualified Data.MonoidMap.Internal as MonoidMap+import qualified Data.Set as Set++spec :: Spec+spec = describe "Accessors" $ do++    forM_ testValueTypesAll $+        \(TestValueType p) -> specFor (Proxy @Key) p++specFor :: forall k v. Test k v => Proxy k -> Proxy v -> Spec+specFor = makeSpec $ do++    describe "Get" $ do+        it "prop_get_nonNullKey" $+            prop_get_nonNullKey+                @k @v & property+        it "prop_get_nonNullKeys" $+            prop_get_nonNullKeys+                @k @v & property++    describe "Set" $ do+        it "prop_set_get" $+            prop_set_get+                @k @v & property+        it "prop_set_nonNullKey" $+            prop_set_nonNullKey+                @k @v & property+        it "prop_set_nonNullKeys" $+            prop_set_nonNullKeys+                @k @v & property+        it "prop_set_toList" $+            prop_set_toList+                @k @v & property++    describe "Adjust" $ do+        it "prop_adjust_get_set" $+            prop_adjust_get_set+                @k @v & property++--------------------------------------------------------------------------------+-- Get+--------------------------------------------------------------------------------++prop_get_nonNullKey+    :: Test k v => MonoidMap k v -> k -> Property+prop_get_nonNullKey m k =+    MonoidMap.nonNullKey k m === (MonoidMap.get k m /= mempty)+    & cover 2+        (MonoidMap.nonNullKey k m)+        "MonoidMap.nonNullKey k m"+    & cover 2+        (not (MonoidMap.nonNullKey k m))+        "not (MonoidMap.nonNullKey k m)"++prop_get_nonNullKeys+    :: Test k v => MonoidMap k v -> k -> Property+prop_get_nonNullKeys m k =+    Set.member k (MonoidMap.nonNullKeys m) === (MonoidMap.get k m /= mempty)+    & cover 2+        (MonoidMap.nonNullKey k m)+        "MonoidMap.nonNullKey k m"+    & cover 2+        (not (MonoidMap.nonNullKey k m))+        "not (MonoidMap.nonNullKey k m)"++--------------------------------------------------------------------------------+-- Set+--------------------------------------------------------------------------------++prop_set_get+    :: Test k v => MonoidMap k v -> k -> v -> Property+prop_set_get m k v =+    MonoidMap.get k (MonoidMap.set k v m) === v+    & cover 2+        (MonoidMap.nonNullKey k m)+        "MonoidMap.nonNullKey k m"+    & cover 2+        (not (MonoidMap.nonNullKey k m))+        "not (MonoidMap.nonNullKey k m)"++prop_set_nonNullKey+    :: Test k v => MonoidMap k v -> k -> v -> Property+prop_set_nonNullKey m k v =+    MonoidMap.nonNullKey k (MonoidMap.set k v m) ===+        (v /= mempty)+    & cover 2+        (v == mempty)+        "v == mempty"+    & cover 2+        (v /= mempty)+        "v /= mempty"++prop_set_nonNullKeys+    :: Test k v => MonoidMap k v -> k -> v -> Property+prop_set_nonNullKeys m k v =+    Set.member k (MonoidMap.nonNullKeys (MonoidMap.set k v m)) ===+        (v /= mempty)+    & cover 2+        (v == mempty)+        "v == mempty"+    & cover 2+        (v /= mempty)+        "v /= mempty"++prop_set_toList+    :: Test k v => MonoidMap k v -> k -> v -> Property+prop_set_toList m k v =+    filter ((== k) . fst) (MonoidMap.toList (MonoidMap.set k v m)) ===+        [(k, v) | v /= mempty]+    & cover 2+        (v == mempty)+        "v == mempty"+    & cover 2+        (v /= mempty)+        "v /= mempty"++--------------------------------------------------------------------------------+-- Adjust+--------------------------------------------------------------------------------++prop_adjust_get_set+    :: Test k v => MonoidMap k v -> Fun v v -> k -> Property+prop_adjust_get_set m (applyFun -> f) k =+    MonoidMap.adjust f k m === MonoidMap.set k (f (MonoidMap.get k m)) m+    & cover 1+        (MonoidMap.nullKey k m && Null.null (f mempty))+        "MonoidMap.nullKey k m && Null.null (f mempty)"+    & cover 1+        (MonoidMap.nullKey k m && not (Null.null (f mempty)))+        "MonoidMap.nullKey k m && not (Null.null (f mempty))"+    & cover 0.1+        (MonoidMap.nonNullKey k m && Null.null (f (MonoidMap.get k m)))+        "MonoidMap.nonNullKey k m && Null.null (f (MonoidMap.get k m))"+    & cover 0.1+        (MonoidMap.nonNullKey k m && not (Null.null (f (MonoidMap.get k m))))+        "MonoidMap.nonNullKey k m && not (Null.null (f (MonoidMap.get k m)))"
+ components/monoidmap-test/Data/MonoidMap/Internal/ClassSpec.hs view
@@ -0,0 +1,336 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.ClassSpec+    where++import Prelude++import Data.Monoid+    ( Product (..), Sum (..) )+import Data.MonoidMap.Internal+    ( MonoidMap )+import Data.Proxy+    ( Proxy (..) )+import Data.Set+    ( Set )+import Data.Typeable+    ( Typeable, typeRep )+import Numeric.Natural+    ( Natural )+import Test.Combinators.NonZero+    ( NonZero, genNonZero, shrinkNonZero )+import Test.Common ()+import Test.Hspec+    ( Spec, describe )+import Test.Key+    ( Key1, Key2, Key4, Key8 )+import Test.QuickCheck+    ( Arbitrary (..) )+import Test.QuickCheck.Classes+    ( eqLaws+    , isListLaws+    , monoidLaws+    , semigroupLaws+    , semigroupMonoidLaws+    , showReadLaws+    )+import Test.QuickCheck.Classes.Group+    ( groupLaws )+import Test.QuickCheck.Classes.Hspec+    ( testLawsMany )+import Test.QuickCheck.Classes.Monoid.GCD+    ( distributiveGCDMonoidLaws+    , gcdMonoidLaws+    , leftDistributiveGCDMonoidLaws+    , leftGCDMonoidLaws+    , overlappingGCDMonoidLaws+    , rightDistributiveGCDMonoidLaws+    , rightGCDMonoidLaws+    )+import Test.QuickCheck.Classes.Monoid.LCM+    ( distributiveLCMMonoidLaws, lcmMonoidLaws )+import Test.QuickCheck.Classes.Monoid.Monus+    ( monusLaws )+import Test.QuickCheck.Classes.Monoid.Null+    ( monoidNullLaws, positiveMonoidLaws )+import Test.QuickCheck.Classes.Semigroup.Cancellative+    ( cancellativeLaws+    , commutativeLaws+    , leftCancellativeLaws+    , leftReductiveLaws+    , reductiveLaws+    , rightCancellativeLaws+    , rightReductiveLaws+    )++spec :: Spec+spec = do+    describe "Class laws" $ do+        -- Test against a variety of key sizes:+        specLawsFor (Proxy @Key1)+        specLawsFor (Proxy @Key2)+        specLawsFor (Proxy @Key4)+        specLawsFor (Proxy @Key8)++specLawsFor+    :: forall k. () =>+        ( Arbitrary k+        , Ord k+        , Read k+        , Show k+        , Typeable k+        )+    => Proxy k+    -> Spec+specLawsFor keyType = do+    let description = "Class laws for key type " <> show (typeRep keyType)+    describe description $ do+        testLawsMany @(MonoidMap k String)+            [ eqLaws+            , isListLaws+            , leftCancellativeLaws+            , leftDistributiveGCDMonoidLaws+            , leftGCDMonoidLaws+            , leftReductiveLaws+            , monoidLaws+            , monoidNullLaws+            , overlappingGCDMonoidLaws+            , positiveMonoidLaws+            , rightCancellativeLaws+            , rightDistributiveGCDMonoidLaws+            , rightGCDMonoidLaws+            , rightReductiveLaws+            , semigroupLaws+            , semigroupMonoidLaws+            , showReadLaws+            ]+        testLawsMany @(MonoidMap k (Product Integer))+            [ commutativeLaws+            , eqLaws+            , isListLaws+            , leftReductiveLaws+            , monoidLaws+            , monoidNullLaws+            , reductiveLaws+            , rightReductiveLaws+            , semigroupLaws+            , semigroupMonoidLaws+            , showReadLaws+            ]+        testLawsMany @(MonoidMap k (Product Natural))+            [ commutativeLaws+            , distributiveGCDMonoidLaws+            , distributiveLCMMonoidLaws+            , eqLaws+            , gcdMonoidLaws+            , lcmMonoidLaws+            , isListLaws+            , leftDistributiveGCDMonoidLaws+            , leftGCDMonoidLaws+            , leftReductiveLaws+            , monoidLaws+            , monoidNullLaws+            , monusLaws+            , overlappingGCDMonoidLaws+            , positiveMonoidLaws+            , reductiveLaws+            , rightDistributiveGCDMonoidLaws+            , rightGCDMonoidLaws+            , rightReductiveLaws+            , semigroupLaws+            , semigroupMonoidLaws+            , showReadLaws+            ]+        -- Here we restrict the generator and shrinker so that they can never+        -- produce zero values, to avoid running into cases of ArithException+        -- caused by operations that may produce zero demoninators:+        testLawsMany @(MonoidMap k (NonZero (Product Rational)))+            [ commutativeLaws+            , eqLaws+            , groupLaws+            , isListLaws+            , monoidLaws+            , monoidNullLaws+            , semigroupLaws+            , semigroupMonoidLaws+            , showReadLaws+            ]+        testLawsMany @(MonoidMap k (Sum Integer))+            [ cancellativeLaws+            , commutativeLaws+            , eqLaws+            , groupLaws+            , isListLaws+            , leftCancellativeLaws+            , leftReductiveLaws+            , monoidLaws+            , monoidNullLaws+            , reductiveLaws+            , rightCancellativeLaws+            , rightReductiveLaws+            , semigroupLaws+            , semigroupMonoidLaws+            , showReadLaws+            ]+        testLawsMany @(MonoidMap k (Sum Natural))+            [ cancellativeLaws+            , commutativeLaws+            , distributiveGCDMonoidLaws+            , distributiveLCMMonoidLaws+            , eqLaws+            , gcdMonoidLaws+            , lcmMonoidLaws+            , isListLaws+            , leftCancellativeLaws+            , leftDistributiveGCDMonoidLaws+            , leftGCDMonoidLaws+            , leftReductiveLaws+            , monoidLaws+            , monoidNullLaws+            , monusLaws+            , overlappingGCDMonoidLaws+            , positiveMonoidLaws+            , reductiveLaws+            , rightCancellativeLaws+            , rightDistributiveGCDMonoidLaws+            , rightGCDMonoidLaws+            , rightReductiveLaws+            , semigroupLaws+            , semigroupMonoidLaws+            , showReadLaws+            ]+        testLawsMany @(MonoidMap k (Set ()))+            [ commutativeLaws+            , distributiveGCDMonoidLaws+            , distributiveLCMMonoidLaws+            , eqLaws+            , gcdMonoidLaws+            , lcmMonoidLaws+            , isListLaws+            , leftDistributiveGCDMonoidLaws+            , leftGCDMonoidLaws+            , leftReductiveLaws+            , monoidLaws+            , monoidNullLaws+            , monusLaws+            , overlappingGCDMonoidLaws+            , positiveMonoidLaws+            , reductiveLaws+            , rightDistributiveGCDMonoidLaws+            , rightGCDMonoidLaws+            , rightReductiveLaws+            , semigroupLaws+            , semigroupMonoidLaws+            , showReadLaws+            ]+        testLawsMany @(MonoidMap k (Set k))+            [ commutativeLaws+            , distributiveGCDMonoidLaws+            , distributiveLCMMonoidLaws+            , eqLaws+            , gcdMonoidLaws+            , lcmMonoidLaws+            , isListLaws+            , leftDistributiveGCDMonoidLaws+            , leftGCDMonoidLaws+            , leftReductiveLaws+            , monoidLaws+            , monoidNullLaws+            , monusLaws+            , overlappingGCDMonoidLaws+            , positiveMonoidLaws+            , reductiveLaws+            , rightDistributiveGCDMonoidLaws+            , rightGCDMonoidLaws+            , rightReductiveLaws+            , semigroupLaws+            , semigroupMonoidLaws+            , showReadLaws+            ]+        testLawsMany @(MonoidMap k (Set Ordering))+            [ commutativeLaws+            , distributiveGCDMonoidLaws+            , distributiveLCMMonoidLaws+            , eqLaws+            , gcdMonoidLaws+            , lcmMonoidLaws+            , isListLaws+            , leftDistributiveGCDMonoidLaws+            , leftGCDMonoidLaws+            , leftReductiveLaws+            , monoidLaws+            , monoidNullLaws+            , monusLaws+            , overlappingGCDMonoidLaws+            , positiveMonoidLaws+            , reductiveLaws+            , rightDistributiveGCDMonoidLaws+            , rightGCDMonoidLaws+            , rightReductiveLaws+            , semigroupLaws+            , semigroupMonoidLaws+            , showReadLaws+            ]+        testLawsMany @(MonoidMap k (Set Int))+            [ commutativeLaws+            , distributiveGCDMonoidLaws+            , distributiveLCMMonoidLaws+            , eqLaws+            , gcdMonoidLaws+            , lcmMonoidLaws+            , isListLaws+            , leftDistributiveGCDMonoidLaws+            , leftGCDMonoidLaws+            , leftReductiveLaws+            , monoidLaws+            , monoidNullLaws+            , monusLaws+            , overlappingGCDMonoidLaws+            , positiveMonoidLaws+            , reductiveLaws+            , rightDistributiveGCDMonoidLaws+            , rightGCDMonoidLaws+            , rightReductiveLaws+            , semigroupLaws+            , semigroupMonoidLaws+            , showReadLaws+            ]+        testLawsMany @(MonoidMap k (MonoidMap k (Sum Natural)))+            [ cancellativeLaws+            , commutativeLaws+            , distributiveGCDMonoidLaws+            , distributiveLCMMonoidLaws+            , eqLaws+            , gcdMonoidLaws+            , lcmMonoidLaws+            , isListLaws+            , leftCancellativeLaws+            , leftDistributiveGCDMonoidLaws+            , leftGCDMonoidLaws+            , leftReductiveLaws+            , monoidLaws+            , monoidNullLaws+            , monusLaws+            , overlappingGCDMonoidLaws+            , positiveMonoidLaws+            , reductiveLaws+            , rightCancellativeLaws+            , rightDistributiveGCDMonoidLaws+            , rightGCDMonoidLaws+            , rightReductiveLaws+            , semigroupLaws+            , semigroupMonoidLaws+            , showReadLaws+            ]++--------------------------------------------------------------------------------+-- Arbitrary instances+--------------------------------------------------------------------------------++instance (Arbitrary a, Eq a, Num a) => Arbitrary (NonZero a) where+    arbitrary = genNonZero arbitrary+    shrink = shrinkNonZero shrink
+ components/monoidmap-test/Data/MonoidMap/Internal/ComparisonSpec.hs view
@@ -0,0 +1,278 @@+{- HLINT ignore "Redundant bracket" -}+{- HLINT ignore "Use camelCase" -}+{- HLINT ignore "Use null" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.ComparisonSpec+    ( spec+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Function+    ( (&) )+import Data.Maybe+    ( isJust )+import Data.Monoid.Cancellative+    ( Reductive (..) )+import Data.Monoid.GCD+    ( GCDMonoid )+import Data.MonoidMap.Internal+    ( MonoidMap )+import Data.Proxy+    ( Proxy (..) )+import Test.Common+    ( Key+    , Test+    , TestValueType (TestValueType)+    , makeSpec+    , property+    , testValueTypesGCDMonoid+    , testValueTypesAll+    , testValueTypesReductive+    )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Fun (..), Property, applyFun2, cover, expectFailure, (.||.), (===) )++import qualified Data.Monoid.GCD as GCDMonoid+    ( GCDMonoid (..) )+import qualified Data.Monoid.Null as Null+    ( MonoidNull (..) )+import qualified Data.MonoidMap.Internal as MonoidMap+import qualified Data.Set as Set++spec :: Spec+spec = describe "Comparison" $ do++    forM_ testValueTypesGCDMonoid $+        \(TestValueType p) -> specGCDMonoid+            (Proxy @Key) p++    forM_ testValueTypesReductive $+        \(TestValueType p) -> specReductive+            (Proxy @Key) p++    forM_ testValueTypesAll $+        \(TestValueType p) -> specMonoidNull+            (Proxy @Key) p++specGCDMonoid+    :: forall k v. (Test k v, GCDMonoid v) => Proxy k -> Proxy v -> Spec+specGCDMonoid = makeSpec $ do+    it "prop_disjoint_gcd" $+        prop_disjoint_gcd+            @k @v & property+    it "prop_disjoint_intersection" $+        prop_disjoint_intersection+            @k @v & property++specReductive+    :: forall k v. (Test k v, Reductive v) => Proxy k -> Proxy v -> Spec+specReductive = makeSpec $ do+    it "prop_isSubmapOf_minusMaybe" $+        prop_isSubmapOf_minusMaybe+            @k @v & property+    it "prop_isSubmapOf_reduce" $+        prop_isSubmapOf_reduce+            @k @v & property++specMonoidNull+    :: forall k v. Test k v => Proxy k -> Proxy v -> Spec+specMonoidNull = makeSpec $ do+    it "prop_disjointBy_get_total" $+        prop_disjointBy_get_total+            @k @v & property+    it "prop_disjointBy_get_total_failure" $+        prop_disjointBy_get_total_failure+            @k @v & property+    it "prop_isSubmapOfBy_get_total" $+        prop_isSubmapOfBy_get_total+            @k @v & property+    it "prop_isSubmapOfBy_get_total_failure" $+        prop_isSubmapOfBy_get_total_failure+            @k @v & property++prop_disjoint_gcd+    :: (Test k v, GCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> k+    -> Property+prop_disjoint_gcd m1 m2 k =+    MonoidMap.disjoint m1 m2 ==>+        (Null.null (GCDMonoid.gcd (MonoidMap.get k m1) (MonoidMap.get k m2)))+    & cover 8+        (MonoidMap.disjoint m1 m2)+        "MonoidMap.disjoint m1 m2"+    & cover 8+        (not (MonoidMap.disjoint m1 m2))+        "not (MonoidMap.disjoint m1 m2)"++prop_disjoint_intersection+    :: (Test k v, GCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+prop_disjoint_intersection m1 m2 =+    MonoidMap.disjoint m1 m2 === (MonoidMap.intersection m1 m2 == mempty)+    & cover 8+        (MonoidMap.disjoint m1 m2)+        "MonoidMap.disjoint m1 m2"+    & cover 8+        (not (MonoidMap.disjoint m1 m2))+        "not (MonoidMap.disjoint m1 m2)"++prop_disjointBy_get_total+    :: Test k v+    => Fun (v, v) Bool+    -> MonoidMap k v+    -> MonoidMap k v+    -> k+    -> Property+prop_disjointBy_get_total (applyFun2 -> f0) m1 m2 k =+    MonoidMap.disjointBy f m1 m2+        ==>+        f (MonoidMap.get k m1) (MonoidMap.get k m2)+    & cover 8+        (m1 /= mempty && m2 /= mempty && MonoidMap.disjointBy f m1 m2)+        "m1 /= mempty && m2 /= mempty && MonoidMap.disjointBy f m1 m2"+    & cover 2+        (keyWithinIntersection)+        "keyWithinIntersection"+    & cover 2+        (not keyWithinIntersection)+        "not keyWithinIntersection"+  where+    keyWithinIntersection =+        k `Set.member` Set.intersection+            (MonoidMap.nonNullKeys m1)+            (MonoidMap.nonNullKeys m2)+    f v1 v2+        | Null.null v1 = True+        | Null.null v2 = True+        | otherwise = f0 v1 v2++prop_disjointBy_get_total_failure+    :: Test k v+    => Fun (v, v) Bool+    -> MonoidMap k v+    -> MonoidMap k v+    -> k+    -> Property+prop_disjointBy_get_total_failure (applyFun2 -> f) m1 m2 k =+    expectFailure $+    MonoidMap.disjointBy f m1 m2+        ==>+        f (MonoidMap.get k m1) (MonoidMap.get k m2)++prop_isSubmapOf_minusMaybe+    :: (Test k v, Reductive v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+prop_isSubmapOf_minusMaybe m1 m2 =+    MonoidMap.isSubmapOf m1 m2+        ==> isJust (m2 `MonoidMap.minusMaybe` m1)+    & cover 0.01+        (nonTrivialSubmap)+        "nonTrivialSubmap"+  where+    nonTrivialSubmap =+        MonoidMap.isSubmapOf m1 m2+        && m1 /= mempty+        && m2 /= mempty+        && m1 /= m2++prop_isSubmapOf_reduce+    :: (Test k v, Reductive v)+    => MonoidMap k v+    -> MonoidMap k v+    -> k+    -> Property+prop_isSubmapOf_reduce m1 m2 k =+    MonoidMap.isSubmapOf m1 m2+        ==> isJust (MonoidMap.get k m2 </> MonoidMap.get k m1)+    & cover 0.001+        (nonTrivialSubmap && nonNullKeyL && nonNullKeyR)+        "nonTrivialSubmap && nonNullKeyL && nonNullKeyR"+    & cover 0.001+        (nonTrivialSubmap && nullKeyL && nonNullKeyR)+        "nonTrivialSubmap && nullKeyL && nonNullKeyR"+    & cover 0.001+        (nonTrivialSubmap && nullKeyL && nullKeyR)+        "nonTrivialSubmap && nullKeyL && nullKeyR"+  where+    nonTrivialSubmap =+        MonoidMap.isSubmapOf m1 m2+        && m1 /= mempty+        && m2 /= mempty+        && m1 /= m2+    nonNullKeyL = MonoidMap.nonNullKey k m1+    nonNullKeyR = MonoidMap.nonNullKey k m2+    nullKeyL = MonoidMap.nullKey k m1+    nullKeyR = MonoidMap.nullKey k m2++prop_isSubmapOfBy_get_total+    :: Test k v+    => Fun (v, v) Bool+    -> MonoidMap k v+    -> MonoidMap k v+    -> k+    -> Property+prop_isSubmapOfBy_get_total (applyFun2 -> f0) m1 m2 k =+    MonoidMap.isSubmapOfBy f m1 m2+        ==>+        f (MonoidMap.get k m1) (MonoidMap.get k m2)+    & cover 0.01+        (nonTrivialSubmap && nonNullKeyL && nonNullKeyR)+        "nonTrivialSubmap && nonNullKeyL && nonNullKeyR"+    & cover 0.1+        (nonTrivialSubmap && nullKeyL && nonNullKeyR)+        "nonTrivialSubmap && nullKeyL && nonNullKeyR"+    & cover 0.1+        (nonTrivialSubmap && nonNullKeyL && nullKeyR)+        "nonTrivialSubmap && nonNullKeyL && nullKeyR"+    & cover 0.1+        (nonTrivialSubmap && nullKeyL && nullKeyR)+        "nonTrivialSubmap && nullKeyL && nullKeyR"+  where+    f v1 v2+        | Null.null v1 = True+        | otherwise = f0 v1 v2+    nonTrivialSubmap =+        MonoidMap.isSubmapOfBy f m1 m2+        && m1 /= mempty+        && m2 /= mempty+        && m1 /= m2+    nonNullKeyL = MonoidMap.nonNullKey k m1+    nonNullKeyR = MonoidMap.nonNullKey k m2+    nullKeyL = MonoidMap.nullKey k m1+    nullKeyR = MonoidMap.nullKey k m2++prop_isSubmapOfBy_get_total_failure+    :: Test k v+    => Fun (v, v) Bool+    -> MonoidMap k v+    -> MonoidMap k v+    -> k+    -> Property+prop_isSubmapOfBy_get_total_failure (applyFun2 -> f) m1 m2 k =+    expectFailure $+    MonoidMap.isSubmapOfBy f m1 m2+        ==>+        f (MonoidMap.get k m1) (MonoidMap.get k m2)++--------------------------------------------------------------------------------+-- Utilities+--------------------------------------------------------------------------------++infixr 3 ==>+(==>) :: Bool -> Bool -> Property+a ==> b = not a .||. b
+ components/monoidmap-test/Data/MonoidMap/Internal/ConversionSpec.hs view
@@ -0,0 +1,267 @@+{- HLINT ignore "Redundant bracket" -}+{- HLINT ignore "Use camelCase" -}+{- HLINT ignore "Use null" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.ConversionSpec+    ( spec+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Function+    ( (&) )+import Data.Map.Strict+    ( Map )+import Data.MonoidMap.Internal+    ( MonoidMap, nonNullCount )+import Data.Proxy+    ( Proxy (..) )+import Data.Set+    ( Set )+import Test.Common+    ( Key+    , Test+    , TestValueType (TestValueType)+    , makeSpec+    , property+    , testValueTypesAll+    )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Fun (..), Property, applyFun, applyFun2, cover, (===) )++import qualified Data.Foldable as F+import qualified Data.List as List+import qualified Data.List.NonEmpty as NE+import qualified Data.Map.Strict as Map+import qualified Data.Monoid.Null as Null+import qualified Data.MonoidMap.Internal as MonoidMap+import qualified Data.Set as Set++spec :: Spec+spec = describe "Conversions" $ do++    forM_ testValueTypesAll $+        \(TestValueType p) -> specFor (Proxy @Key) p++specFor :: forall k v. Test k v => Proxy k -> Proxy v -> Spec+specFor = makeSpec $ do++    describe "Conversion to and from lists" $ do+        it "prop_fromList_get" $+            prop_fromList_get+                @k @v & property+        it "prop_fromList_toMap" $+            prop_fromList_toMap+                @k @v & property+        it "prop_fromList_toList" $+            prop_fromList_toList+                @k @v & property+        it "prop_toList_fromList" $+            prop_toList_fromList+                @k @v & property+        it "prop_toList_sort" $+            prop_toList_sort+                @k @v & property+        it "prop_fromListWith_get" $+            prop_fromListWith_get+                @k @v & property++    describe "Conversion to and from ordinary maps" $ do+        it "prop_fromMap_get" $+            prop_fromMap_get+                @k @v & property+        it "prop_fromMap_toMap" $+            prop_fromMap_toMap+                @k @v & property+        it "prop_fromMapWith_fromMap" $+            prop_fromMapWith_fromMap+                @k @v & property+        it "prop_fromMapWith_get" $+            prop_fromMapWith_get+                @k @v & property+        it "prop_toMap_fromMap" $+            prop_toMap_fromMap+                @k @v & property++    describe "Conversion from sets" $ do+        it "prop_fromSet_get" $+            prop_fromSet_get+                @k @v & property++--------------------------------------------------------------------------------+-- Conversion to and from lists+--------------------------------------------------------------------------------++prop_fromList_get+    :: Test k v => [(k, v)] -> k -> Property+prop_fromList_get kvs k =+    MonoidMap.get k (MonoidMap.fromList kvs)+        ===+        F.foldMap snd (filter ((== k) . fst) kvs)+    & cover 2+        (matchingKeyCount == 0)+        "matchingKeyCount == 0"+    & cover 2+        (matchingKeyCount == 1)+        "matchingKeyCount == 1"+    & cover 2+        (matchingKeyCount == 2)+        "matchingKeyCount == 2"+    & cover 2+        (matchingKeyCount >= 3)+        "matchingKeyCount >= 3"+  where+    matchingKeyCount =+        length $ filter ((== k) . fst) kvs++prop_fromList_toMap+    :: Test k v => [(k, v)] -> Property+prop_fromList_toMap kvs =+    MonoidMap.toMap m === Map.filter (/= mempty) o+    & cover 2+        (MonoidMap.nonNull m && nonNullCount m /= Map.size o)+        "MonoidMap.nonNull m && nonNullCount m /= Map.size o"+    & cover 2+        (MonoidMap.nonNull m && nonNullCount m == Map.size o)+        "MonoidMap.nonNull m && nonNullCount m == Map.size o"+  where+    m = MonoidMap.fromList kvs+    o = Map.fromListWith (flip (<>)) kvs++prop_fromList_toList+    :: Test k v => [(k, v)] -> Property+prop_fromList_toList kvs =+    MonoidMap.toList m === Map.toList (Map.filter (/= mempty) o)+    & cover 2+        (MonoidMap.nonNull m && nonNullCount m /= Map.size o)+        "MonoidMap.nonNull m && nonNullCount m /= Map.size o"+    & cover 2+        (MonoidMap.nonNull m && nonNullCount m == Map.size o)+        "MonoidMap.nonNull m && nonNullCount m == Map.size o"+  where+    m = MonoidMap.fromList kvs+    o = Map.fromListWith (flip (<>)) kvs++prop_toList_fromList+    :: Test k v => MonoidMap k v -> Property+prop_toList_fromList m =+    MonoidMap.fromList (MonoidMap.toList m) === m+    & cover 2+        (MonoidMap.nonNull m)+        "MonoidMap.nonNull m"++prop_toList_sort+    :: Test k v => MonoidMap k v -> Property+prop_toList_sort m =+    List.sortOn fst (MonoidMap.toList m) === MonoidMap.toList m+    & cover 2+        (MonoidMap.nonNull m)+        "MonoidMap.nonNull m"++prop_fromListWith_get+    :: Test k v => Fun (v, v) v -> [(k, v)] -> k -> Property+prop_fromListWith_get (applyFun2 -> f) kvs k =+    MonoidMap.get k (MonoidMap.fromListWith f kvs)+        ===+        maybe mempty+            (F.foldl1 f)+            (NE.nonEmpty (snd <$> filter ((== k) . fst) kvs))+    & cover 2+        (matchingKeyCount == 0)+        "matchingKeyCount == 0"+    & cover 2+        (matchingKeyCount == 1)+        "matchingKeyCount == 1"+    & cover 2+        (matchingKeyCount == 2)+        "matchingKeyCount == 2"+    & cover 2+        (matchingKeyCount >= 3)+        "matchingKeyCount >= 3"+  where+    matchingKeyCount =+        length $ filter ((== k) . fst) kvs++--------------------------------------------------------------------------------+-- Conversion to and from ordinary maps+--------------------------------------------------------------------------------++prop_fromMap_get+    :: Test k v => Map k v -> k -> Property+prop_fromMap_get m k =+    MonoidMap.get k (MonoidMap.fromMap m) === Map.findWithDefault mempty k m+    & cover 2+        (MonoidMap.get k (MonoidMap.fromMap m) /= mempty)+        "MonoidMap.get k (MonoidMap.fromMap m) /= mempty"+    & cover 0.1+        (MonoidMap.get k (MonoidMap.fromMap m) == mempty && Map.member k m)+        "MonoidMap.get k (MonoidMap.fromMap m) == mempty && Map.member k m"++prop_fromMap_toMap+    :: Test k v => Map k v -> Property+prop_fromMap_toMap o =+    MonoidMap.toMap m === Map.filter (/= mempty) o+    & cover 2+        (MonoidMap.nonNull m && nonNullCount m /= Map.size o)+        "MonoidMap.nonNull m && nonNullCount m /= Map.size o"+    & cover 2+        (MonoidMap.nonNull m && nonNullCount m == Map.size o)+        "MonoidMap.nonNull m && nonNullCount m == Map.size o"+  where+    m = MonoidMap.fromMap o++prop_fromMapWith_fromMap+    :: Test k v => Map k v -> Property+prop_fromMapWith_fromMap m =+    MonoidMap.fromMapWith id m === MonoidMap.fromMap m+    & cover 2+        (MonoidMap.nonNull (MonoidMap.fromMap m))+        "MonoidMap.nonNull (MonoidMap.fromMap m)"++prop_fromMapWith_get+    :: Test k v => Fun v v -> Map k v -> k -> Property+prop_fromMapWith_get (applyFun -> f) m k =+    MonoidMap.get k (MonoidMap.fromMapWith f m)+        === maybe mempty f (Map.lookup k m)+    & cover 2+        (MonoidMap.nonNullKey k (MonoidMap.fromMapWith f m))+        "MonoidMap.nonNullKey k (MonoidMap.fromMapWith f m)"+    & cover 0.01+        (MonoidMap.nullKey k (MonoidMap.fromMapWith f m) && Map.member k m)+        "MonoidMap.nullKey k (MonoidMap.fromMapWith f m) && Map.member k m"++prop_toMap_fromMap+    :: Test k v => MonoidMap k v -> Property+prop_toMap_fromMap m =+    MonoidMap.fromMap (MonoidMap.toMap m) === m++--------------------------------------------------------------------------------+-- Conversion from sets+--------------------------------------------------------------------------------++prop_fromSet_get+    :: Test k v => Fun k v -> Set k -> k -> Property+prop_fromSet_get (applyFun -> f) ks k =+    MonoidMap.get k (MonoidMap.fromSet f ks)+        ===+        (if Set.member k ks then f k else mempty)+    & cover 0.2+        (Set.member k ks && Null.null (f k))+        "Set.member k ks && Null.null (f k)"+    & cover 8.0+        (Set.member k ks && not (Null.null (f k)))+        "Set.member k ks && not (Null.null (f k))"+    & cover 0.2+        (not (Set.member k ks) && Null.null (f k))+        "not (Set.member k ks) && Null.null (f k)"+    & cover 8.0+        (not (Set.member k ks) && not (Null.null (f k)))+        "not (Set.member k ks) && not (Null.null (f k))"
+ components/monoidmap-test/Data/MonoidMap/Internal/DistributivitySpec.hs view
@@ -0,0 +1,230 @@+{-# LANGUAGE RankNTypes #-}+{- HLINT ignore "Redundant bracket" -}+{- HLINT ignore "Use camelCase" -}+{- HLINT ignore "Use null" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.DistributivitySpec+    ( spec+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Data+    ( typeRep )+import Data.Function+    ( (&) )+import Data.Maybe+    ( isJust )+import Data.MonoidMap.Internal+    ( MonoidMap, get )+import Data.Proxy+    ( Proxy (..) )+import Test.Common+    ( Key+    , Test+    , TestValueType (..)+    , TestValue+    , property+    , testValueTypesGCDMonoid+    , testValueTypesGroup+    , testValueTypesLCMMonoid+    , testValueTypesLeftGCDMonoid+    , testValueTypesLeftReductive+    , testValueTypesAll+    , testValueTypesMonus+    , testValueTypesOverlappingGCDMonoid+    , testValueTypesReductive+    , testValueTypesRightGCDMonoid+    , testValueTypesRightReductive+    )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Property, cover, (===) )++import qualified Data.Group as Group+    ( Group (..) )+import qualified Data.Monoid.GCD as LeftGCDMonoid+    ( LeftGCDMonoid (..) )+import qualified Data.Monoid.GCD as RightGCDMonoid+    ( RightGCDMonoid (..) )+import qualified Data.Monoid.GCD as OverlappingGCDMonoid+    ( OverlappingGCDMonoid (..) )+import qualified Data.Monoid.GCD as GCDMonoid+    ( GCDMonoid (..) )+import qualified Data.Monoid.LCM as LCMMonoid+    ( LCMMonoid (..) )+import qualified Data.Monoid.Monus as Monus+    ( Monus (..) )+import qualified Data.Semigroup as Semigroup+    ( Semigroup (..) )+import qualified Data.Semigroup.Cancellative as LeftReductive+    ( LeftReductive (..) )+import qualified Data.Semigroup.Cancellative as RightReductive+    ( RightReductive (..) )+import qualified Data.Semigroup.Cancellative as Reductive+    ( Reductive (..) )++spec :: Spec+spec = do+    specDistributiveGet+    specDistributiveGetMaybe++specDistributiveGet :: Spec+specDistributiveGet = do+    specForAll+        testValueTypesAll+        "Semigroup.<>"+        (Semigroup.<>)+        (Semigroup.<>)+    specForAll+        testValueTypesLeftGCDMonoid+        "LeftGCDMonoid.commonPrefix"+        (LeftGCDMonoid.commonPrefix)+        (LeftGCDMonoid.commonPrefix)+    specForAll+        testValueTypesRightGCDMonoid+        "RightGCDMonoid.commonSuffix"+        (RightGCDMonoid.commonSuffix)+        (RightGCDMonoid.commonSuffix)+    specForAll+        testValueTypesOverlappingGCDMonoid+        "OverlappingGCDMonoid.overlap"+        (OverlappingGCDMonoid.overlap)+        (OverlappingGCDMonoid.overlap)+    specForAll+        testValueTypesGCDMonoid+        "GCDMonoid.gcd"+        (GCDMonoid.gcd)+        (GCDMonoid.gcd)+    specForAll+        testValueTypesLCMMonoid+        "LCMMonoid.lcm"+        (LCMMonoid.lcm)+        (LCMMonoid.lcm)+    specForAll+        testValueTypesGroup+        "Group.minus"+        (Group.~~)+        (Group.~~)+    specForAll+        testValueTypesMonus+        "Monus.monus"+        (Monus.<\>)+        (Monus.<\>)+  where+    specForAll+        :: [TestValueType c]+        -> String+        -> (forall k v m. (Test k v, c v, m ~ MonoidMap k v) => (m -> m -> m))+        -> (forall v. (TestValue v, c v) => (v -> v -> v))+        -> Spec+    specForAll testValueTypes funName f g =+        describe description $ forM_ testValueTypes $ specFor f g+      where+        description = "Distributivity of 'get' with '" <> funName <> "'"++    specFor+        :: (forall k v m. (Test k v, c v, m ~ MonoidMap k v) => (m -> m -> m))+        -> (forall v. (TestValue v, c v) => (v -> v -> v))+        -> TestValueType c+        -> Spec+    specFor f g (TestValueType (_ :: Proxy v)) =+        it description $ property $ propDistributiveGet @Key @v f g+      where+        description = show $ typeRep $ Proxy @(MonoidMap Key v)++specDistributiveGetMaybe :: Spec+specDistributiveGetMaybe = do+    specForAll+        testValueTypesLeftReductive+        "LeftReductive.stripPrefix"+        (LeftReductive.stripPrefix)+        (LeftReductive.stripPrefix)+    specForAll+        testValueTypesRightReductive+        "RightReductive.stripSuffix"+        (RightReductive.stripSuffix)+        (RightReductive.stripSuffix)+    specForAll+        testValueTypesReductive+        "Reductive.minusMaybe"+        (Reductive.</>)+        (Reductive.</>)+  where+    specForAll+        :: [TestValueType c]+        -> String+        -> (forall k v m. (Test k v, c v, m ~ MonoidMap k v)+            => (m -> m -> Maybe m))+        -> (forall v. (TestValue v, c v)+            => (v -> v -> Maybe v))+        -> Spec+    specForAll testValueTypes funName f g =+        describe description $ forM_ testValueTypes $ specFor f g+      where+        description = "Distributivity of 'get' with '" <> funName <> "'"++    specFor+        :: (forall k v m. (Test k v, c v, m ~ MonoidMap k v)+            => (m -> m -> Maybe m))+        -> (forall v. (TestValue v, c v)+            => (v -> v -> Maybe v))+        -> TestValueType c+        -> Spec+    specFor f g (TestValueType (_ :: Proxy v)) =+        it description $ property $ propDistributiveGetMaybe @Key @v f g+      where+        description = show $ typeRep $ Proxy @(MonoidMap Key v)++propDistributiveGet+    :: Test k v+    => (MonoidMap k v -> MonoidMap k v -> MonoidMap k v)+    -> (v -> v -> v)+    -> k+    -> MonoidMap k v+    -> MonoidMap k v+    -> Property+propDistributiveGet f g k m1 m2 =+    get k (f m1 m2) === g (get k m1) (get k m2)+    & cover 2+        (get k (f m1 m2) == mempty)+        "get k (f m1 m2) == mempty"+    & cover 2+        (get k (f m1 m2) /= mempty)+        "get k (f m1 m2) /= mempty"+    & cover 2+        (get k m1 == mempty && get k m2 == mempty)+        "get k m1 == mempty && get k m2 == mempty"+    & cover 2+        (get k m1 == mempty && get k m2 /= mempty)+        "get k m1 == mempty && get k m2 /= mempty"+    & cover 2+        (get k m1 /= mempty && get k m2 == mempty)+        "get k m1 /= mempty && get k m2 == mempty"+    & cover 2+        (get k m1 /= mempty && get k m2 /= mempty)+        "get k m1 /= mempty && get k m2 /= mempty"++propDistributiveGetMaybe+    :: Test k v+    => (MonoidMap k v -> MonoidMap k v -> Maybe (MonoidMap k v))+    -> (v -> v -> Maybe v)+    -> k+    -> MonoidMap k v+    -> MonoidMap k v+    -> Property+propDistributiveGetMaybe f g k m1 m2 = property $+    all (\m -> g (get k m1) (get k m2) == Just (get k m)) (f m1 m2)+    & cover 2+        (isJust (f m1 m2) && g (get k m1) (get k m2) == Just mempty)+        "isJust (f m1 m2) && g (get k m1) (get k m2) == Just mempty"+    & cover 2+        (isJust (f m1 m2) && g (get k m1) (get k m2) /= Just mempty)+        "isJust (f m1 m2) && g (get k m1) (get k m2) /= Just mempty"
+ components/monoidmap-test/Data/MonoidMap/Internal/ExampleSpec.hs view
@@ -0,0 +1,1738 @@+{-# LANGUAGE OverloadedLists #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{- HLINT ignore "Redundant bracket" -}+{- HLINT ignore "Use camelCase" -}+{- HLINT ignore "Use null" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.ExampleSpec+    where++import Prelude hiding+    ( gcd, lcm )++import Data.Function+    ( (&) )+import Data.Group+    ( Group (..) )+import Data.Monoid+    ( Product (..), Sum (..) )+import Data.Monoid.GCD+    ( GCDMonoid (..), LeftGCDMonoid (..), RightGCDMonoid (..) )+import Data.Monoid.LCM+    ( LCMMonoid (..) )+import Data.Monoid.Monus+    ( OverlappingGCDMonoid (..), (<\>) )+import Data.MonoidMap.Internal+    ( MonoidMap )+import Data.Ratio+    ( (%) )+import Data.Semigroup.Cancellative+    ( LeftReductive (..), RightReductive (..) )+import Data.Set+    ( Set )+import Numeric.Natural+    ( Natural )+import Test.Common+    ()+import Test.Hspec+    ( Spec, describe )+import Test.Hspec.Unit+    ( UnitTestData1+    , UnitTestData2+    , unitTestData1+    , unitTestData2+    , unitTestSpec+    )++import qualified Data.MonoidMap.Internal as MonoidMap+import qualified Data.Set as Set++spec :: Spec+spec = describe "Examples" $ do++    describe "Conversion" $ do++        exampleSpec_fromList_String+        exampleSpec_toList_String++    describe "Comparison" $ do++        exampleSpec_isSubmapOf_Sum_Natural+        exampleSpec_disjoint_Product_Natural+        exampleSpec_disjoint_Sum_Natural+        exampleSpec_disjoint_Set_Natural++    describe "Intersection" $ do++        exampleSpec_intersectionWith_min_Sum_Natural++    describe "Union" $ do++        exampleSpec_unionWith_max_Sum_Natural++    describe "Semigroup" $ do++        exampleSpec_Semigroup_mappend_String+        exampleSpec_Semigroup_mappend_Sum_Natural++    describe "Group" $ do++        exampleSpec_Group_invert_Product_Rational+        exampleSpec_Group_invert_Sum_Integer+        exampleSpec_Group_pow_Product_Rational+        exampleSpec_Group_pow_Sum_Integer+        exampleSpec_Group_subtract_Product_Rational+        exampleSpec_Group_subtract_Sum_Integer++    describe "Reductive" $ do++        exampleSpec_Reductive_isPrefixOf_String+        exampleSpec_Reductive_isPrefixOf_Sum_Natural+        exampleSpec_Reductive_isSuffixOf_String+        exampleSpec_Reductive_isSuffixOf_Sum_Natural+        exampleSpec_Reductive_stripPrefix_String+        exampleSpec_Reductive_stripPrefix_Sum_Natural+        exampleSpec_Reductive_stripSuffix_String+        exampleSpec_Reductive_stripSuffix_Sum_Natural++    describe "LeftGCDMonoid" $ do++        exampleSpec_LeftGCDMonoid_commonPrefix_String+        exampleSpec_LeftGCDMonoid_commonPrefix_Sum_Natural+        exampleSpec_LeftGCDMonoid_stripCommonPrefix_String+        exampleSpec_LeftGCDMonoid_stripCommonPrefix_Sum_Natural++    describe "RightGCDMonoid" $ do++        exampleSpec_RightGCDMonoid_commonSuffix_String+        exampleSpec_RightGCDMonoid_commonSuffix_Sum_Natural+        exampleSpec_RightGCDMonoid_stripCommonSuffix_String+        exampleSpec_RightGCDMonoid_stripCommonSuffix_Sum_Natural++    describe "OverlappingGCDMonoid" $ do++        exampleSpec_OverlappingGCDMonoid_overlap_String+        exampleSpec_OverlappingGCDMonoid_overlap_Sum_Natural+        exampleSpec_OverlappingGCDMonoid_stripPrefixOverlap_String+        exampleSpec_OverlappingGCDMonoid_stripPrefixOverlap_Sum_Natural+        exampleSpec_OverlappingGCDMonoid_stripSuffixOverlap_String+        exampleSpec_OverlappingGCDMonoid_stripSuffixOverlap_Sum_Natural++    describe "GCDMonoid" $ do++        exampleSpec_GCDMonoid_gcd_Product_Natural+        exampleSpec_GCDMonoid_gcd_Sum_Natural+        exampleSpec_GCDMonoid_gcd_Set_Natural++    describe "LCMMonoid" $ do++        exampleSpec_LCMMonoid_lcm_Product_Natural+        exampleSpec_LCMMonoid_lcm_Sum_Natural+        exampleSpec_LCMMonoid_lcm_Set_Natural++    describe "Monus" $ do++        exampleSpec_Monus_monus_Set_Natural+        exampleSpec_Monus_monus_Sum_Natural++--------------------------------------------------------------------------------+-- Conversion+--------------------------------------------------------------------------------++exampleSpec_fromList_String :: Spec+exampleSpec_fromList_String = unitTestSpec+    "MonoidMap.fromList (String)"+    "MonoidMap.fromList"+    (MonoidMap.fromList)+    (exampleData_fromList_String)++exampleData_fromList_String :: UnitTestData1+    [(Int, String)]+    (MonoidMap Int String)+exampleData_fromList_String = unitTestData1+    [ ( [(1, "a"), (2, "x"), (1, "b"), (2, "y"), (1, "c"), (2, "z")]+      , [(1, "abc"), (2, "xyz")]+      )+    ]++exampleSpec_toList_String :: Spec+exampleSpec_toList_String = unitTestSpec+    "MonoidMap.toList (String)"+    "MonoidMap.toList"+    (MonoidMap.toList)+    (exampleData_toList_String)++exampleData_toList_String :: UnitTestData1+    (MonoidMap Int String)+    [(Int, String)]+exampleData_toList_String = unitTestData1+    [ ( [(3, "z"), (2, "y"), (1, "x")]+      , [(1, "x"), (2, "y"), (3, "z")]+      )+    ]++--------------------------------------------------------------------------------+-- Comparison+--------------------------------------------------------------------------------++exampleSpec_isSubmapOf_Sum_Natural :: Spec+exampleSpec_isSubmapOf_Sum_Natural = unitTestSpec+    "MonoidMap.isSubmapOf (Sum Natural)"+    "MonoidMap.isSubmapOf"+    (MonoidMap.isSubmapOf)+    (exampleData_isSubmapOf_Sum_Natural)++exampleData_isSubmapOf_Sum_Natural :: UnitTestData2+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+    (Bool)+exampleData_isSubmapOf_Sum_Natural = unitTestData2+    [ ( m [0, 1, 2, 3]+      , m [4, 4, 4, 4]+      , True+      )+    , ( m [0, 1, 2, 3]+      , m [0, 4, 4, 4]+      , True+      )+    , ( m [0, 1, 2, 3]+      , m [0, 1, 4, 4]+      , True+      )+    , ( m [0, 1, 2, 3]+      , m [0, 1, 2, 4]+      , True+      )+    , ( m [0, 1, 2, 3]+      , m [0, 1, 2, 3]+      , True+      )+    , ( m [0, 1, 2, 3]+      , m [0, 0, 2, 3]+      , False+      )+    , ( m [0, 1, 2, 3]+      , m [0, 1, 1, 3]+      , False+      )+    , ( m [0, 1, 2, 3]+      , m [0, 1, 2, 2]+      , False+      )+    , ( m [0, 1, 2, 3]+      , m [0, 0, 0, 0]+      , False+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_disjoint_Product_Natural :: Spec+exampleSpec_disjoint_Product_Natural = unitTestSpec+    "MonoidMap.disjoint (Product Natural)"+    "MonoidMap.disjoint"+    (MonoidMap.disjoint)+    (exampleData_disjoint_Product_Natural)++exampleData_disjoint_Product_Natural :: UnitTestData2+    (MonoidMap LatinChar (Product Natural))+    (MonoidMap LatinChar (Product Natural))+    (Bool)+exampleData_disjoint_Product_Natural = unitTestData2+    [ ( m []+      , m []+      , True+      )+    , ( m [2, 3, 5, 7]+      , m [3, 5, 7, 2]+      , True+      )+    , ( m [2 * 3, 5 * 7]+      , m [5 * 7, 2 * 3]+      , True+      )+    , ( m [2 * 3    , 3 * 5    ]+      , m [    3 * 5,     5 * 7]+      , False+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_disjoint_Sum_Natural :: Spec+exampleSpec_disjoint_Sum_Natural = unitTestSpec+    "MonoidMap.disjoint (Sum Natural)"+    "MonoidMap.disjoint"+    (MonoidMap.disjoint)+    (exampleData_disjoint_Sum_Natural)++exampleData_disjoint_Sum_Natural :: UnitTestData2+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+    (Bool)+exampleData_disjoint_Sum_Natural = unitTestData2+    [ ( m []+      , m []+      , True+      )+    , ( m [0, 1, 0, 1]+      , m [1, 0, 1, 0]+      , True+      )+    , ( m [0, 8, 0, 8]+      , m [8, 0, 8, 0]+      , True+      )+    , ( m [0, 8, 0, 8]+      , m [8, 0, 8, 1]+      , False+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_disjoint_Set_Natural :: Spec+exampleSpec_disjoint_Set_Natural = unitTestSpec+    "MonoidMap.disjoint (Set Natural)"+    "MonoidMap.disjoint"+    (MonoidMap.disjoint)+    (exampleData_disjoint_Set_Natural)++exampleData_disjoint_Set_Natural :: UnitTestData2+    (MonoidMap LatinChar (Set Natural))+    (MonoidMap LatinChar (Set Natural))+    (Bool)+exampleData_disjoint_Set_Natural = unitTestData2+    [ ( m []+      , m []+      , True+      )+    , ( m [[1], [2], [3], [4]]+      , m [[5], [6], [7], [8]]+      , True+      )+    , ( m [[1, 2], [3, 4]]+      , m [[5, 6], [7, 8]]+      , True+      )+    , ( m [[1, 2   ], [3, 4   ]]+      , m [[   2, 3], [   4, 5]]+      , False+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..] . fmap Set.fromList++--------------------------------------------------------------------------------+-- Intersection+--------------------------------------------------------------------------------++exampleSpec_intersectionWith_min_Sum_Natural :: Spec+exampleSpec_intersectionWith_min_Sum_Natural = unitTestSpec+    "MonoidMap.intersectionWith (Sum Natural)"+    "MonoidMap.intersectionWith"+    (MonoidMap.intersectionWith min)+    (exampleData_intersectionWith_min_Sum_Natural)++exampleData_intersectionWith_min_Sum_Natural :: UnitTestData2+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+exampleData_intersectionWith_min_Sum_Natural = unitTestData2+    [ ( m [0, 1, 2, 3, 4, 5, 6, 7]+      , m [7, 6, 5, 4, 3, 2, 1, 0]+      , m [0, 1, 2, 3, 3, 2, 1, 0]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++--------------------------------------------------------------------------------+-- Union+--------------------------------------------------------------------------------++exampleSpec_unionWith_max_Sum_Natural :: Spec+exampleSpec_unionWith_max_Sum_Natural = unitTestSpec+    "MonoidMap.unionWith (Sum Natural)"+    "MonoidMap.unionWith"+    (MonoidMap.unionWith max)+    (exampleData_unionWith_max_Sum_Natural)++exampleData_unionWith_max_Sum_Natural :: UnitTestData2+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+exampleData_unionWith_max_Sum_Natural = unitTestData2+    [ ( m [0, 1, 2, 3, 4, 5, 6, 7]+      , m [7, 6, 5, 4, 3, 2, 1, 0]+      , m [7, 6, 5, 4, 4, 5, 6, 7]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++--------------------------------------------------------------------------------+-- Semigroup+--------------------------------------------------------------------------------++exampleSpec_Semigroup_mappend_String :: Spec+exampleSpec_Semigroup_mappend_String = unitTestSpec+    "Semigroup.mappend (String)"+    "mappend"+    (mappend)+    (exampleData_Semigroup_concat_String)++exampleData_Semigroup_concat_String :: UnitTestData2+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+exampleData_Semigroup_concat_String = unitTestData2+    [ ( m ["abc", "ij" , "p"  , ""   ]+      , m [   "",   "k",  "qr", "xyz"]+      , m ["abc", "ijk", "pqr", "xyz"]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_Semigroup_mappend_Sum_Natural :: Spec+exampleSpec_Semigroup_mappend_Sum_Natural = unitTestSpec+    "Semigroup.mappend (Sum Natural)"+    "mappend"+    (mappend)+    (exampleData_Semigroup_concat_Sum_Natural)++exampleData_Semigroup_concat_Sum_Natural :: UnitTestData2+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+exampleData_Semigroup_concat_Sum_Natural = unitTestData2+    [ ( m [4, 2, 1, 0]+      , m [0, 1, 2, 4]+      , m [4, 3, 3, 4]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++--------------------------------------------------------------------------------+-- Group+--------------------------------------------------------------------------------++exampleSpec_Group_invert_Product_Rational :: Spec+exampleSpec_Group_invert_Product_Rational = unitTestSpec+    "Group.invert (Product Rational)"+    "invert"+    (invert)+    (exampleData_Group_invert_Product_Rational)++exampleData_Group_invert_Product_Rational :: UnitTestData1+    (MonoidMap LatinChar (Product Rational))+    (MonoidMap LatinChar (Product Rational))+exampleData_Group_invert_Product_Rational = unitTestData1+    [ ( m [  2,   4,   8,   16]+      , m [1%2, 1%4, 1%8, 1%16]+      )+    , ( m [1%2, 1%4, 1%8, 1%16]+      , m [  2,   4,   8,   16]+      )+    , ( m [  2, 1%4,   8,   16]+      , m [1%2,   4, 1%8, 1%16]+      )+    , ( m [1%2,   4, 1%8, 1%16]+      , m [  2, 1%4,   8,   16]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..] . fmap Product++exampleSpec_Group_invert_Sum_Integer :: Spec+exampleSpec_Group_invert_Sum_Integer = unitTestSpec+    "Group.invert (Sum Integer)"+    "invert"+    (invert)+    (exampleData_Group_invert_Sum_Integer)++exampleData_Group_invert_Sum_Integer :: UnitTestData1+    (MonoidMap LatinChar (Sum Integer))+    (MonoidMap LatinChar (Sum Integer))+exampleData_Group_invert_Sum_Integer = unitTestData1+    [ ( m [ 1,  2,  3,  4]+      , m [-1, -2, -3, -4]+      )+    , ( m [-1, -2, -3, -4]+      , m [ 1,  2,  3,  4]+      )+    , ( m [ 1, -2,  3, -4]+      , m [-1,  2, -3,  4]+      )+    , ( m [-1,  2, -3,  4]+      , m [ 1, -2,  3, -4]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..] . fmap Sum++exampleSpec_Group_pow_Product_Rational :: Spec+exampleSpec_Group_pow_Product_Rational = unitTestSpec+    "Group.pow (Product Rational)"+    "pow"+    (pow)+    (exampleData_Group_pow_Product_Rational)++exampleData_Group_pow_Product_Rational :: UnitTestData2+    (MonoidMap LatinChar (Product Rational))+    (Integer)+    (MonoidMap LatinChar (Product Rational))+exampleData_Group_pow_Product_Rational = unitTestData2+    [ ( m [  2,   -4,   8,   -16], (-1)+      , m [1%2, -1%4, 1%8, -1%16]+      )+    , ( m [  2,   -4,   8,   -16], 0+      , m [  1,    1,   1,     1]+      )+    , ( m [  2,   -4,   8,   -16], 1+      , m [  2,   -4,   8,   -16]+      )+    , ( m [  2,   -4,   8,   -16], 2+      , m [  4,   16,  64,   256]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..] . fmap Product++exampleSpec_Group_pow_Sum_Integer :: Spec+exampleSpec_Group_pow_Sum_Integer = unitTestSpec+    "Group.pow (Sum Integer)"+    "pow"+    (pow)+    (exampleData_Group_pow_Sum_Integer)++exampleData_Group_pow_Sum_Integer :: UnitTestData2+    (MonoidMap LatinChar (Sum Integer))+    (Integer)+    (MonoidMap LatinChar (Sum Integer))+exampleData_Group_pow_Sum_Integer = unitTestData2+    [ ( m [ 1, -2,  3, -4], (-1)+      , m [-1,  2, -3,  4]+      )+    , ( m [ 1, -2,  3, -4], 0+      , m [ 0,  0,  0,  0]+      )+    , ( m [ 1, -2,  3, -4], 1+      , m [ 1, -2,  3, -4]+      )+    , ( m [ 1, -2,  3, -4], 2+      , m [ 2, -4,  6, -8]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..] . fmap Sum++exampleSpec_Group_subtract_Product_Rational :: Spec+exampleSpec_Group_subtract_Product_Rational = unitTestSpec+    "Group.(~~) (Product Rational)"+    "(~~)"+    (~~)+    (exampleData_Group_subtract_Product_Rational)++exampleData_Group_subtract_Product_Rational :: UnitTestData2+    (MonoidMap LatinChar (Product Rational))+    (MonoidMap LatinChar (Product Rational))+    (MonoidMap LatinChar (Product Rational))+exampleData_Group_subtract_Product_Rational = unitTestData2+    [ ( m [ 1,    1,    1,    1]+      , m [ 1,    2,    4,    8]+      , m [ 1,  1%2,  1%4,  1%8]+      )+    , ( m [-1,   -1,   -1,   -1]+      , m [ 1,    2,    4,    8]+      , m [-1, -1%2, -1%4, -1%8]+      )+    , ( m [ 1,    1,    1,    1]+      , m [-1,   -2,   -4,   -8]+      , m [-1, -1%2, -1%4, -1%8]+      )+    , ( m [-1,   -1,   -1,   -1]+      , m [-1,   -2,   -4,   -8]+      , m [ 1,  1%2,  1%4,  1%8]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..] . fmap Product++exampleSpec_Group_subtract_Sum_Integer :: Spec+exampleSpec_Group_subtract_Sum_Integer = unitTestSpec+    "Group.(~~) (Sum Integer)"+    "(~~)"+    (~~)+    (exampleData_Group_subtract_Sum_Integer)++exampleData_Group_subtract_Sum_Integer :: UnitTestData2+    (MonoidMap LatinChar (Sum Integer))+    (MonoidMap LatinChar (Sum Integer))+    (MonoidMap LatinChar (Sum Integer))+exampleData_Group_subtract_Sum_Integer = unitTestData2+    [ ( m [ 1,  2,  3,  4]+      , m [ 1,  2,  3,  4]+      , m [ 0,  0,  0,  0]+      )+    , ( m [ 0,  0,  0,  0]+      , m [ 1,  2,  3,  4]+      , m [-1, -2, -3, -4]+      )+    , ( m [ 1,  2,  3,  4]+      , m [-1, -2, -3, -4]+      , m [ 2,  4,  6,  8]+      )+    , ( m [-1, -2, -3, -4]+      , m [-1, -2, -3, -4]+      , m [ 0,  0,  0,  0]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..] . fmap Sum++--------------------------------------------------------------------------------+-- Reductive+--------------------------------------------------------------------------------++exampleSpec_Reductive_isPrefixOf_String :: Spec+exampleSpec_Reductive_isPrefixOf_String = unitTestSpec+    "Reductive.isPrefixOf (String)"+    "isPrefixOf"+    (isPrefixOf)+    (exampleData_Reductive_isPrefixOf_String)++exampleData_Reductive_isPrefixOf_String :: UnitTestData2+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+    (Bool)+exampleData_Reductive_isPrefixOf_String = unitTestData2+    [ ( m ["A"   , "B"   , "C"   ]+      , m ["A123", "B123", "C123"]+      , True+      )+    , ( m ["A123", "B123", "C123"]+      , m ["A"   , "B"   , "C"   ]+      , False+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_Reductive_isSuffixOf_String :: Spec+exampleSpec_Reductive_isSuffixOf_String = unitTestSpec+    "Reductive.isSuffixOf (String)"+    "isSuffixOf"+    (isSuffixOf)+    (exampleData_Reductive_isSuffixOf_String)++exampleData_Reductive_isSuffixOf_String :: UnitTestData2+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+    (Bool)+exampleData_Reductive_isSuffixOf_String = unitTestData2+    [ ( m [   "A",    "B",    "C"]+      , m ["123A", "123B", "123C"]+      , True+      )+    , ( m ["123A", "123B", "123C"]+      , m [   "A",    "B",    "C"]+      , False+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_Reductive_isPrefixOf_Sum_Natural :: Spec+exampleSpec_Reductive_isPrefixOf_Sum_Natural = unitTestSpec+    "Reductive.isPrefixOf (Sum Natural)"+    "isPrefixOf"+    (isPrefixOf)+    (exampleData_Reductive_Sum_Natural)++exampleSpec_Reductive_isSuffixOf_Sum_Natural :: Spec+exampleSpec_Reductive_isSuffixOf_Sum_Natural = unitTestSpec+    "Reductive.isSuffixOf (Sum Natural)"+    "isSuffixOf"+    (isSuffixOf)+    (exampleData_Reductive_Sum_Natural)++exampleData_Reductive_Sum_Natural :: UnitTestData2+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+    (Bool)+exampleData_Reductive_Sum_Natural = unitTestData2+    [ ( m [1, 1], m [1, 1], True )+    , ( m [1, 1], m [1, 2], True )+    , ( m [1, 1], m [2, 1], True )+    , ( m [1, 1], m [2, 2], True )+    , ( m [1, 2], m [1, 1], False)+    , ( m [1, 2], m [1, 2], True )+    , ( m [1, 2], m [2, 1], False)+    , ( m [1, 2], m [2, 2], True )+    , ( m [2, 1], m [1, 1], False)+    , ( m [2, 1], m [1, 2], False)+    , ( m [2, 1], m [2, 1], True )+    , ( m [2, 1], m [2, 2], True )+    , ( m [2, 2], m [1, 1], False)+    , ( m [2, 2], m [1, 2], False)+    , ( m [2, 2], m [2, 1], False)+    , ( m [2, 2], m [2, 2], True )+    ]+  where+    m = MonoidMap.fromList . zip [A ..] . fmap Sum++exampleSpec_Reductive_stripPrefix_String :: Spec+exampleSpec_Reductive_stripPrefix_String = unitTestSpec+    "Reductive.stripPrefix (String)"+    "stripPrefix"+    (stripPrefix)+    (exampleData_Reductive_stripPrefix_String)++exampleData_Reductive_stripPrefix_String :: UnitTestData2+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+    (Maybe (MonoidMap LatinChar String))+exampleData_Reductive_stripPrefix_String = unitTestData2+    [ ( m [""   , ""   , ""   ]+      , m ["abc", "pqr", "xyz"]+      , m ["abc", "pqr", "xyz"] & Just+      )+    , ( m ["a"  , "p"  , "x"  ]+      , m ["abc", "pqr", "xyz"]+      , m [ "bc",  "qr",  "yz"] & Just+      )+    , ( m ["abc", "pqr", "xyz"]+      , m ["abc", "pqr", "xyz"]+      , m [   "",    "",    ""] & Just+      )+    , ( m ["?"  , "p"  , "x"  ]+      , m ["abc", "pqr", "xyz"]+      , Nothing+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_Reductive_stripPrefix_Sum_Natural :: Spec+exampleSpec_Reductive_stripPrefix_Sum_Natural = unitTestSpec+    "Reductive.stripPrefix (Sum Natural)"+    "stripPrefix"+    (stripPrefix)+    (exampleData_Reductive_stripPrefix_Sum_Natural)++exampleData_Reductive_stripPrefix_Sum_Natural :: UnitTestData2+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+    (Maybe (MonoidMap LatinChar (Sum Natural)))+exampleData_Reductive_stripPrefix_Sum_Natural = unitTestData2+    [ ( m [0, 0, 0]+      , m [2, 4, 8]+      , m [2, 4, 8] & Just+      )+    , ( m [1, 2, 4]+      , m [2, 4, 8]+      , m [1, 2, 4] & Just+      )+    , ( m [2, 4, 8]+      , m [2, 4, 8]+      , m [0, 0, 0] & Just+      )+    , ( m [3, 4, 8]+      , m [2, 4, 8]+      , Nothing+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_Reductive_stripSuffix_String :: Spec+exampleSpec_Reductive_stripSuffix_String = unitTestSpec+    "Reductive.stripSuffix (String)"+    "stripSuffix"+    (stripSuffix)+    (exampleData_Reductive_stripSuffix_String)++exampleData_Reductive_stripSuffix_String :: UnitTestData2+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+    (Maybe (MonoidMap LatinChar String))+exampleData_Reductive_stripSuffix_String = unitTestData2+    [ ( m [   "",    "",    ""]+      , m ["abc", "pqr", "xyz"]+      , m ["abc", "pqr", "xyz"] & Just+      )+    , ( m [  "c",   "r",   "z"]+      , m ["abc", "pqr", "xyz"]+      , m ["ab" , "pq" , "xy" ] & Just+      )+    , ( m ["abc", "pqr", "xyz"]+      , m ["abc", "pqr", "xyz"]+      , m [""   , ""   , ""   ] & Just+      )+    , ( m [  "?",   "r",   "z"]+      , m ["abc", "pqr", "xyz"]+      , Nothing+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_Reductive_stripSuffix_Sum_Natural :: Spec+exampleSpec_Reductive_stripSuffix_Sum_Natural = unitTestSpec+    "Reductive.stripSuffix (Sum Natural)"+    "stripSuffix"+    (stripSuffix)+    (exampleData_Reductive_stripSuffix_Sum_Natural)++exampleData_Reductive_stripSuffix_Sum_Natural :: UnitTestData2+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+    (Maybe (MonoidMap LatinChar (Sum Natural)))+exampleData_Reductive_stripSuffix_Sum_Natural = unitTestData2+    [ ( m [0, 0, 0]+      , m [2, 4, 8]+      , m [2, 4, 8] & Just+      )+    , ( m [1, 2, 4]+      , m [2, 4, 8]+      , m [1, 2, 4] & Just+      )+    , ( m [2, 4, 8]+      , m [2, 4, 8]+      , m [0, 0, 0] & Just+      )+    , ( m [3, 4, 8]+      , m [2, 4, 8]+      , Nothing+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++--------------------------------------------------------------------------------+-- LeftGCDMonoid+--------------------------------------------------------------------------------++exampleSpec_LeftGCDMonoid_commonPrefix_String :: Spec+exampleSpec_LeftGCDMonoid_commonPrefix_String = unitTestSpec+    "LeftGCDMonoid.commonPrefix (String)"+    "commonPrefix"+    (commonPrefix)+    (exampleData_LeftGCDMonoid_commonPrefix_String)++exampleData_LeftGCDMonoid_commonPrefix_String :: UnitTestData2+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+exampleData_LeftGCDMonoid_commonPrefix_String = unitTestData2+    [ ( m ["---", "---", "---"]+      , m ["abc", "pqr", "xyz"]+      , m [""   , ""   , ""   ]+      )+    , ( m ["a--", "p--", "x--"]+      , m ["abc", "pqr", "xyz"]+      , m ["a"  , "p"  , "x"  ]+      )+    , ( m ["ab-", "pq-", "xy-"]+      , m ["abc", "pqr", "xyz"]+      , m ["ab" , "pq" , "xy" ]+      )+    , ( m ["abc", "pqr", "xyz"]+      , m ["abc", "pqr", "xyz"]+      , m ["abc", "pqr", "xyz"]+      )+    , ( m ["abc", "pqr", "xyz"]+      , m ["ab-", "pq-", "xy-"]+      , m ["ab" , "pq" , "xy" ]+      )+    , ( m ["abc", "pqr", "xyz"]+      , m ["a--", "p--", "x--"]+      , m ["a"  , "p"  , "x"  ]+      )+    , ( m ["abc", "pqr", "xyz"]+      , m ["---", "---", "---"]+      , m [""   , ""   , ""   ]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_LeftGCDMonoid_commonPrefix_Sum_Natural :: Spec+exampleSpec_LeftGCDMonoid_commonPrefix_Sum_Natural = unitTestSpec+    "LeftGCDMonoid.commonPrefix (Sum Natural)"+    "commonPrefix"+    (commonPrefix)+    (exampleData_LeftGCDMonoid_commonPrefix_Sum_Natural)++exampleData_LeftGCDMonoid_commonPrefix_Sum_Natural :: UnitTestData2+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+exampleData_LeftGCDMonoid_commonPrefix_Sum_Natural = unitTestData2+    [ ( m [0, 0, 0]+      , m [1, 2, 3]+      , m [0, 0, 0]+      )+    , ( m [1, 1, 1]+      , m [1, 2, 3]+      , m [1, 1, 1]+      )+    , ( m [2, 2, 2]+      , m [1, 2, 3]+      , m [1, 2, 2]+      )+    , ( m [3, 3, 3]+      , m [1, 2, 3]+      , m [1, 2, 3]+      )+    , ( m [4, 4, 4]+      , m [1, 2, 3]+      , m [1, 2, 3]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_LeftGCDMonoid_stripCommonPrefix_String :: Spec+exampleSpec_LeftGCDMonoid_stripCommonPrefix_String = unitTestSpec+    "LeftGCDMonoid.stripCommonPrefix (String)"+    "stripCommonPrefix"+    (stripCommonPrefix)+    (exampleData_LeftGCDMonoid_stripCommonPrefix_String)++exampleData_LeftGCDMonoid_stripCommonPrefix_String :: UnitTestData2+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+    ( MonoidMap LatinChar String+    , MonoidMap LatinChar String+    , MonoidMap LatinChar String+    )+exampleData_LeftGCDMonoid_stripCommonPrefix_String = unitTestData2+    [ (   m ["---", "---", "---"]+      ,   m ["abc", "pqr", "xyz"]++      , ( m [""   , ""   , ""   ]+        , m ["---", "---", "---"]+        , m ["abc", "pqr", "xyz"]+        )+      )+    , (   m ["a--", "p--", "x--"]+      ,   m ["abc", "pqr", "xyz"]++      , ( m ["a"  , "p"  , "x"  ]+        , m [ "--",  "--",  "--"]+        , m [ "bc",  "qr",  "yz"]+        )+      )+    , (   m ["ab-", "pq-", "xy-"]+      ,   m ["abc", "pqr", "xyz"]++      , ( m ["ab" , "pq" , "xy" ]+        , m [  "-",   "-",   "-"]+        , m [  "c",   "r",   "z"]+        )+      )+    , (   m ["abc", "pqr", "xyz"]+      ,   m ["abc", "pqr", "xyz"]++      , ( m ["abc", "pqr", "xyz"]+        , m [   "",    "",    ""]+        , m [   "",    "",    ""]+        )+      )+    , (   m ["abc", "pqr", "xyz"]+      ,   m ["ab-", "pq-", "xy-"]++      , ( m ["ab" , "pq" , "xy" ]+        , m [  "c",   "r",   "z"]+        , m [  "-",   "-",   "-"]+        )+      )+    , (   m ["abc", "pqr", "xyz"]+      ,   m ["a--", "p--", "x--"]+      , ( m ["a"  , "p"  , "x"  ]+        , m [ "bc",  "qr",  "yz"]+        , m [ "--",  "--",  "--"]+        )+      )+    , (   m ["abc", "pqr", "xyz"]+      ,   m ["---", "---", "---"]+      , ( m [""   , ""   , ""   ]+        , m ["abc", "pqr", "xyz"]+        , m ["---", "---", "---"]+        )+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_LeftGCDMonoid_stripCommonPrefix_Sum_Natural :: Spec+exampleSpec_LeftGCDMonoid_stripCommonPrefix_Sum_Natural = unitTestSpec+    "LeftGCDMonoid.stripCommonPrefix (Sum Natural)"+    "stripCommonPrefix"+    (stripCommonPrefix)+    (exampleData_LeftGCDMonoid_stripCommonPrefix_Sum_Natural)++exampleData_LeftGCDMonoid_stripCommonPrefix_Sum_Natural :: UnitTestData2+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+    ( MonoidMap LatinChar (Sum Natural)+    , MonoidMap LatinChar (Sum Natural)+    , MonoidMap LatinChar (Sum Natural)+    )+exampleData_LeftGCDMonoid_stripCommonPrefix_Sum_Natural = unitTestData2+    [ (   m [0, 1, 2, 3, 4]+      ,   m [4, 3, 2, 1, 0]++      , ( m [0, 1, 2, 1, 0]+        , m [0, 0, 0, 2, 4]+        , m [4, 2, 0, 0, 0]+        )+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++--------------------------------------------------------------------------------+-- RightGCDMonoid+--------------------------------------------------------------------------------++exampleSpec_RightGCDMonoid_commonSuffix_String :: Spec+exampleSpec_RightGCDMonoid_commonSuffix_String = unitTestSpec+    "RightGCDMonoid.commonSuffix (String)"+    "commonSuffix"+    (commonSuffix)+    (exampleData_RightGCDMonoid_commonSuffix_String)++exampleData_RightGCDMonoid_commonSuffix_String :: UnitTestData2+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+exampleData_RightGCDMonoid_commonSuffix_String = unitTestData2+    [ ( m ["---", "---", "---"]+      , m ["abc", "pqr", "xyz"]+      , m [  "" ,    "",    ""]+      )+    , ( m ["--c", "--r", "--z"]+      , m ["abc", "pqr", "xyz"]+      , m [  "c",   "r",   "z"]+      )+    , ( m ["-bc", "-qr", "-yz"]+      , m ["abc", "pqr", "xyz"]+      , m [ "bc",  "qr",  "yz"]+      )+    , ( m ["abc", "pqr", "xyz"]+      , m ["abc", "pqr", "xyz"]+      , m ["abc", "pqr", "xyz"]+      )+    , ( m ["abc", "pqr", "xyz"]+      , m ["-bc", "-qr", "-yz"]+      , m [ "bc",  "qr",  "yz"]+      )+    , ( m ["abc", "pqr", "xyz"]+      , m ["--c", "--r", "--z"]+      , m [  "c",   "r",   "z"]+      )+    , ( m ["abc", "pqr", "xyz"]+      , m ["---", "---", "---"]+      , m [   "",    "",    ""]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_RightGCDMonoid_commonSuffix_Sum_Natural :: Spec+exampleSpec_RightGCDMonoid_commonSuffix_Sum_Natural = unitTestSpec+    "RightGCDMonoid.commonSuffix (Sum Natural)"+    "commonSuffix"+    (commonSuffix)+    (exampleData_RightGCDMonoid_commonSuffix_Sum_Natural)++exampleData_RightGCDMonoid_commonSuffix_Sum_Natural :: UnitTestData2+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+exampleData_RightGCDMonoid_commonSuffix_Sum_Natural = unitTestData2+    [ ( m [0, 0, 0]+      , m [1, 2, 3]+      , m [0, 0, 0]+      )+    , ( m [1, 1, 1]+      , m [1, 2, 3]+      , m [1, 1, 1]+      )+    , ( m [2, 2, 2]+      , m [1, 2, 3]+      , m [1, 2, 2]+      )+    , ( m [3, 3, 3]+      , m [1, 2, 3]+      , m [1, 2, 3]+      )+    , ( m [4, 4, 4]+      , m [1, 2, 3]+      , m [1, 2, 3]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_RightGCDMonoid_stripCommonSuffix_String :: Spec+exampleSpec_RightGCDMonoid_stripCommonSuffix_String = unitTestSpec+    "RightGCDMonoid.stripCommonSuffix (String)"+    "stripCommonSuffix"+    (stripCommonSuffix)+    (exampleData_RightGCDMonoid_stripCommonSuffix_String)++exampleData_RightGCDMonoid_stripCommonSuffix_String :: UnitTestData2+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+    ( MonoidMap LatinChar String+    , MonoidMap LatinChar String+    , MonoidMap LatinChar String+    )+exampleData_RightGCDMonoid_stripCommonSuffix_String = unitTestData2+    [ (   m ["---", "---", "---"]+      ,   m ["abc", "pqr", "xyz"]++      , ( m ["---", "---", "---"]+        , m ["abc", "pqr", "xyz"]+        , m [   "",    "",    ""]+        )+      )+    , (   m ["--c", "--r", "--z"]+      ,   m ["abc", "pqr", "xyz"]++      , ( m ["--" , "--" , "--" ]+        , m ["ab" , "pq" , "xy" ]+        , m [  "c",   "r",   "z"]+        )+      )+    , (   m ["--c", "--r", "--z"]+      ,   m ["abc", "pqr", "xyz"]++      , ( m ["--" , "--" , "--" ]+        , m ["ab" , "pq" , "xy" ]+        , m [  "c",   "r",   "z"]+        )+      )+    , (   m ["-bc", "-qr", "-yz"]+      ,   m ["abc", "pqr", "xyz"]++      , ( m ["-"  , "-"  , "-"  ]+        , m ["a"  , "p"  , "x"  ]+        , m [ "bc",  "qr",  "yz"]+        )+      )+    , (   m ["abc", "pqr", "xyz"]+      ,   m ["abc", "pqr", "xyz"]++      , ( m [""   , ""   , ""   ]+        , m [""   , ""   , ""   ]+        , m ["abc", "pqr", "xyz"]+        )+      )+    , (   m ["abc", "pqr", "xyz"]+      ,   m ["-bc", "-qr", "-yz"]++      , ( m ["a"  , "p"  , "x"  ]+        , m ["-"  , "-"  , "-"  ]+        , m [ "bc",  "qr",  "yz"]+        )+      )+    , (   m ["abc", "pqr", "xyz"]+      ,   m ["--c", "--r", "--z"]++      , ( m ["ab" , "pq" , "xy" ]+        , m ["--" , "--" , "--" ]+        , m [  "c",   "r",   "z"]+        )+      )+    , (   m ["abc", "pqr", "xyz"]+      ,   m ["---", "---", "---"]++      , ( m ["abc", "pqr", "xyz"]+        , m ["---", "---", "---"]+        , m [   "",    "",    ""]+        )+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_RightGCDMonoid_stripCommonSuffix_Sum_Natural :: Spec+exampleSpec_RightGCDMonoid_stripCommonSuffix_Sum_Natural = unitTestSpec+    "RightGCDMonoid.stripCommonSuffix (Sum Natural)"+    "stripCommonSuffix"+    (stripCommonSuffix)+    (exampleData_RightGCDMonoid_stripCommonSuffix_Sum_Natural)++exampleData_RightGCDMonoid_stripCommonSuffix_Sum_Natural :: UnitTestData2+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+    ( MonoidMap LatinChar (Sum Natural)+    , MonoidMap LatinChar (Sum Natural)+    , MonoidMap LatinChar (Sum Natural)+    )+exampleData_RightGCDMonoid_stripCommonSuffix_Sum_Natural = unitTestData2+    [ (   m [0, 1, 2, 3, 4]+      ,   m [4, 3, 2, 1, 0]++      , ( m [0, 0, 0, 2, 4]+        , m [4, 2, 0, 0, 0]+        , m [0, 1, 2, 1, 0]+        )+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++--------------------------------------------------------------------------------+-- OverlappingGCDMonoid+--------------------------------------------------------------------------------++exampleSpec_OverlappingGCDMonoid_overlap_String :: Spec+exampleSpec_OverlappingGCDMonoid_overlap_String = unitTestSpec+    "OverlappingGCDMonoid.overlap (String)"+    "overlap"+    (overlap)+    (exampleData_OverlappingGCDMonoid_overlap_String)++exampleData_OverlappingGCDMonoid_overlap_String :: UnitTestData2+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+exampleData_OverlappingGCDMonoid_overlap_String = unitTestData2+    [ ( m ["abcd"    , "0123"    ]+      , m [    "efgh",     "4567"]+      , m [    ""    ,     ""    ]+      )+    , ( m ["abcde"   , "01234"   ]+      , m [   "defgh",    "34567"]+      , m [   "de"   ,    "34"   ]+      )+    , ( m ["abcdef"  , "012345"  ]+      , m [  "cdefgh",   "234567"]+      , m [  "cdef"  ,   "2345"  ]+      )+    , ( m ["abcdefg" , "0123456" ]+      , m [ "bcdefgh",  "1234567"]+      , m [ "bcdefg" ,  "123456" ]+      )+    , ( m ["abcdefgh", "01234567"]+      , m ["abcdefgh", "01234567"]+      , m ["abcdefgh", "01234567"]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_OverlappingGCDMonoid_overlap_Sum_Natural :: Spec+exampleSpec_OverlappingGCDMonoid_overlap_Sum_Natural = unitTestSpec+    "OverlappingGCDMonoid.overlap (Sum Natural)"+    "overlap"+    (overlap)+    (exampleData_OverlappingGCDMonoid_overlap_Sum_Natural)++exampleData_OverlappingGCDMonoid_overlap_Sum_Natural :: UnitTestData2+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+exampleData_OverlappingGCDMonoid_overlap_Sum_Natural = unitTestData2+    [ ( m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]+      , m [0, 1, 2, 3, 4, 4, 3, 2, 1, 0]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_OverlappingGCDMonoid_stripPrefixOverlap_String :: Spec+exampleSpec_OverlappingGCDMonoid_stripPrefixOverlap_String = unitTestSpec+    "OverlappingGCDMonoid.stripPrefixOverlap (String)"+    "stripPrefixOverlap"+    (stripPrefixOverlap)+    (exampleData_OverlappingGCDMonoid_stripPrefixOverlap_String)++exampleData_OverlappingGCDMonoid_stripPrefixOverlap_String :: UnitTestData2+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+exampleData_OverlappingGCDMonoid_stripPrefixOverlap_String = unitTestData2+    [ ( m ["abcd"    , "0123"    ]+      , m [    "efgh",     "4567"]+      , m [    "efgh",     "4567"]+      )+    , ( m ["abcde"   , "01234"   ]+      , m [   "defgh",    "34567"]+      , m [     "fgh",      "567"]+      )+    , ( m ["abcdef"  , "012345"  ]+      , m [  "cdefgh",   "234567"]+      , m [      "gh",       "67"]+      )+    , ( m ["abcdefg" , "0123456" ]+      , m [ "bcdefgh",  "1234567"]+      , m [       "h",        "7"]+      )+    , ( m ["abcdefgh", "01234567"]+      , m ["abcdefgh", "01234567"]+      , m [        "",         ""]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_OverlappingGCDMonoid_stripSuffixOverlap_String :: Spec+exampleSpec_OverlappingGCDMonoid_stripSuffixOverlap_String = unitTestSpec+    "OverlappingGCDMonoid.stripSuffixOverlap (String)"+    "stripSuffixOverlap"+    (stripSuffixOverlap)+    (exampleData_OverlappingGCDMonoid_stripSuffixOverlap_String)++exampleData_OverlappingGCDMonoid_stripSuffixOverlap_String :: UnitTestData2+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+    (MonoidMap LatinChar String)+exampleData_OverlappingGCDMonoid_stripSuffixOverlap_String = unitTestData2+    [ ( m [    "efgh",     "4567"]+      , m ["abcd"    , "0123"    ]+      , m ["abcd"    , "0123"    ]+      )+    , ( m [   "defgh",    "34567"]+      , m ["abcde"   , "01234"   ]+      , m ["abc"     , "012"     ]+      )+    , ( m [  "cdefgh",   "234567"]+      , m ["abcdef"  , "012345"  ]+      , m ["ab"      , "01"      ]+      )+    , ( m [ "bcdefgh",  "1234567"]+      , m ["abcdefg" , "0123456" ]+      , m ["a"       , "0"       ]+      )+    , ( m ["abcdefgh", "01234567"]+      , m ["abcdefgh", "01234567"]+      , m [""        , ""        ]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_OverlappingGCDMonoid_stripPrefixOverlap_Sum_Natural :: Spec+exampleSpec_OverlappingGCDMonoid_stripPrefixOverlap_Sum_Natural = unitTestSpec+    "OverlappingGCDMonoid.stripPrefixOverlap (Sum Natural)"+    "stripPrefixOverlap"+    (stripPrefixOverlap)+    (exampleData_OverlappingGCDMonoid_stripPrefixOverlap_Sum_Natural)++exampleData_OverlappingGCDMonoid_stripPrefixOverlap_Sum_Natural+    :: UnitTestData2+        (MonoidMap LatinChar (Sum Natural))+        (MonoidMap LatinChar (Sum Natural))+        (MonoidMap LatinChar (Sum Natural))+exampleData_OverlappingGCDMonoid_stripPrefixOverlap_Sum_Natural = unitTestData2+    [ ( m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]+      , m [9, 7, 5, 3, 1, 0, 0, 0, 0, 0]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_OverlappingGCDMonoid_stripSuffixOverlap_Sum_Natural :: Spec+exampleSpec_OverlappingGCDMonoid_stripSuffixOverlap_Sum_Natural = unitTestSpec+    "OverlappingGCDMonoid.stripSuffixOverlap (Sum Natural)"+    "stripSuffixOverlap"+    (stripSuffixOverlap)+    (exampleData_OverlappingGCDMonoid_stripSuffixOverlap_Sum_Natural)++exampleData_OverlappingGCDMonoid_stripSuffixOverlap_Sum_Natural+    :: UnitTestData2+        (MonoidMap LatinChar (Sum Natural))+        (MonoidMap LatinChar (Sum Natural))+        (MonoidMap LatinChar (Sum Natural))+exampleData_OverlappingGCDMonoid_stripSuffixOverlap_Sum_Natural = unitTestData2+    [ ( m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]+      , m [9, 7, 5, 3, 1, 0, 0, 0, 0, 0]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++--------------------------------------------------------------------------------+-- GCDMonoid+--------------------------------------------------------------------------------++exampleSpec_GCDMonoid_gcd_Product_Natural :: Spec+exampleSpec_GCDMonoid_gcd_Product_Natural = unitTestSpec+    "GCDMonoid.gcd (Product Natural)"+    "gcd"+    (gcd)+    (exampleData_GCDMonoid_gcd_Product_Natural)++exampleData_GCDMonoid_gcd_Product_Natural :: UnitTestData2+    (MonoidMap LatinChar (Product Natural))+    (MonoidMap LatinChar (Product Natural))+    (MonoidMap LatinChar (Product Natural))+exampleData_GCDMonoid_gcd_Product_Natural = unitTestData2+    [ ( m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]+      , m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      )+    , ( m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]+      , m [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]+      )+    , ( m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [2, 2, 2, 2, 2, 2, 2, 2, 2, 2]+      , m [2, 1, 2, 1, 2, 1, 2, 1, 2, 1]+      )+    , ( m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [3, 3, 3, 3, 3, 3, 3, 3, 3, 3]+      , m [3, 1, 1, 3, 1, 1, 3, 1, 1, 3]+      )+    , ( m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [4, 4, 4, 4, 4, 4, 4, 4, 4, 4]+      , m [4, 1, 2, 1, 4, 1, 2, 1, 4, 1]+      )+    , ( m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [5, 5, 5, 5, 5, 5, 5, 5, 5, 5]+      , m [5, 1, 1, 1, 1, 5, 1, 1, 1, 1]+      )+    , ( m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [6, 6, 6, 6, 6, 6, 6, 6, 6, 6]+      , m [6, 1, 2, 3, 2, 1, 6, 1, 2, 3]+      )+    , ( m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [7, 7, 7, 7, 7, 7, 7, 7, 7, 7]+      , m [7, 1, 1, 1, 1, 1, 1, 7, 1, 1]+      )+    , ( m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [8, 8, 8, 8, 8, 8, 8, 8, 8, 8]+      , m [8, 1, 2, 1, 4, 1, 2, 1, 8, 1]+      )+    , ( m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [9, 9, 9, 9, 9, 9, 9, 9, 9, 9]+      , m [9, 1, 1, 3, 1, 1, 3, 1, 1, 9]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_GCDMonoid_gcd_Sum_Natural :: Spec+exampleSpec_GCDMonoid_gcd_Sum_Natural = unitTestSpec+    "GCDMonoid.gcd (Sum Natural)"+    "gcd"+    (gcd)+    (exampleData_GCDMonoid_gcd_Sum_Natural)++exampleData_GCDMonoid_gcd_Sum_Natural :: UnitTestData2+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+exampleData_GCDMonoid_gcd_Sum_Natural = unitTestData2+    [ ( m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]+      , m [0, 1, 2, 3, 4, 4, 3, 2, 1, 0]+      )+    , ( m [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]+      , m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [0, 1, 2, 3, 4, 4, 3, 2, 1, 0]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_GCDMonoid_gcd_Set_Natural :: Spec+exampleSpec_GCDMonoid_gcd_Set_Natural = unitTestSpec+    "GCDMonoid.gcd (Set Natural)"+    "gcd"+    (gcd)+    (exampleData_GCDMonoid_gcd_Set_Natural)++exampleData_GCDMonoid_gcd_Set_Natural :: UnitTestData2+    (MonoidMap LatinChar (Set Natural))+    (MonoidMap LatinChar (Set Natural))+    (MonoidMap LatinChar (Set Natural))+exampleData_GCDMonoid_gcd_Set_Natural = unitTestData2+    [ ( m [[0, 1, 2, 3], [4, 5, 6, 7]]+      , m [[0, 1, 2, 3], [4, 5, 6, 7]]+      , m [[0, 1, 2, 3], [4, 5, 6, 7]]+      )+    , ( m [[0, 1, 2, 3], [4, 5, 6, 7]]+      , m [[          ], [          ]]+      , m [[          ], [          ]]+      )+    , ( m [[          ], [          ]]+      , m [[0, 1, 2, 3], [4, 5, 6, 7]]+      , m [[          ], [          ]]+      )+    , ( m [[0, 1, 2, 3], [4, 5, 6, 7]]+      , m [[   1, 2, 3], [   5, 6, 7]]+      , m [[   1, 2, 3], [   5, 6, 7]]+      )+    , ( m [[   1, 2, 3], [   5, 6, 7]]+      , m [[0, 1, 2, 3], [4, 5, 6, 7]]+      , m [[   1, 2, 3], [   5, 6, 7]]+      )+    , ( m [[0, 1, 2   ], [4, 5, 6   ]]+      , m [[   1, 2, 3], [   5, 6, 7]]+      , m [[   1, 2   ], [   5, 6   ]]+      )+    , ( m [[   1, 2, 3], [   5, 6, 7]]+      , m [[0, 1, 2   ], [4, 5, 6   ]]+      , m [[   1, 2   ], [   5, 6   ]]+      )+    , ( m [[0, 1      ], [4, 5      ]]+      , m [[      2, 3], [      6, 7]]+      , m [[          ], [          ]]+      )+    , ( m [[      2, 3], [      6, 7]]+      , m [[0, 1      ], [4, 5      ]]+      , m [[          ], [          ]]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..] . fmap Set.fromList++--------------------------------------------------------------------------------+-- LCMMonoid+--------------------------------------------------------------------------------++exampleSpec_LCMMonoid_lcm_Product_Natural :: Spec+exampleSpec_LCMMonoid_lcm_Product_Natural = unitTestSpec+    "LCMMonoid.lcm (Product Natural)"+    "lcm"+    (lcm)+    (exampleData_LCMMonoid_lcm_Product_Natural)++exampleData_LCMMonoid_lcm_Product_Natural :: UnitTestData2+    (MonoidMap LatinChar (Product Natural))+    (MonoidMap LatinChar (Product Natural))+    (MonoidMap LatinChar (Product Natural))+exampleData_LCMMonoid_lcm_Product_Natural = unitTestData2+    [ ( m [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9]+      , m [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0]+      , m [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0]+      )+    , ( m [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9]+      , m [ 1,  1,  1,  1,  1,  1,  1,  1,  1,  1]+      , m [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9]+      )+    , ( m [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9]+      , m [ 2,  2,  2,  2,  2,  2,  2,  2,  2,  2]+      , m [ 0,  2,  2,  6,  4, 10,  6, 14,  8, 18]+      )+    , ( m [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9]+      , m [ 3,  3,  3,  3,  3,  3,  3,  3,  3,  3]+      , m [ 0,  3,  6,  3, 12, 15,  6, 21, 24,  9]+      )+    , ( m [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9]+      , m [ 4,  4,  4,  4,  4,  4,  4,  4,  4,  4]+      , m [ 0,  4,  4, 12,  4, 20, 12, 28,  8, 36]+      )+    , ( m [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9]+      , m [ 5,  5,  5,  5,  5,  5,  5,  5,  5,  5]+      , m [ 0,  5, 10, 15, 20,  5, 30, 35, 40, 45]+      )+    , ( m [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9]+      , m [ 6,  6,  6,  6,  6,  6,  6,  6,  6,  6]+      , m [ 0,  6,  6,  6, 12, 30,  6, 42, 24, 18]+      )+    , ( m [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9]+      , m [ 7,  7,  7,  7,  7,  7,  7,  7,  7,  7]+      , m [ 0,  7, 14, 21, 28, 35, 42,  7, 56, 63]+      )+    , ( m [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9]+      , m [ 8,  8,  8,  8,  8,  8,  8,  8,  8,  8]+      , m [ 0,  8,  8, 24,  8, 40, 24, 56,  8, 72]+      )+    , ( m [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9]+      , m [ 9,  9,  9,  9,  9,  9,  9,  9,  9,  9]+      , m [ 0,  9, 18,  9, 36, 45, 18, 63, 72,  9]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_LCMMonoid_lcm_Sum_Natural :: Spec+exampleSpec_LCMMonoid_lcm_Sum_Natural = unitTestSpec+    "LCMMonoid.lcm (Sum Natural)"+    "lcm"+    (lcm)+    (exampleData_LCMMonoid_lcm_Sum_Natural)++exampleData_LCMMonoid_lcm_Sum_Natural :: UnitTestData2+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+exampleData_LCMMonoid_lcm_Sum_Natural = unitTestData2+    [ ( m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]+      , m [9, 8, 7, 6, 5, 5, 6, 7, 8, 9]+      )+    , ( m [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]+      , m [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]+      , m [9, 8, 7, 6, 5, 5, 6, 7, 8, 9]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++exampleSpec_LCMMonoid_lcm_Set_Natural :: Spec+exampleSpec_LCMMonoid_lcm_Set_Natural = unitTestSpec+    "LCMMonoid.lcm (Set Natural)"+    "lcm"+    (lcm)+    (exampleData_LCMMonoid_lcm_Set_Natural)++exampleData_LCMMonoid_lcm_Set_Natural :: UnitTestData2+    (MonoidMap LatinChar (Set Natural))+    (MonoidMap LatinChar (Set Natural))+    (MonoidMap LatinChar (Set Natural))+exampleData_LCMMonoid_lcm_Set_Natural = unitTestData2+    [ ( m [[0, 1, 2, 3], [4, 5, 6, 7]]+      , m [[0, 1, 2, 3], [4, 5, 6, 7]]+      , m [[0, 1, 2, 3], [4, 5, 6, 7]]+      )+    , ( m [[0, 1, 2, 3], [4, 5, 6, 7]]+      , m [[          ], [          ]]+      , m [[0, 1, 2, 3], [4, 5, 6, 7]]+      )+    , ( m [[          ], [          ]]+      , m [[0, 1, 2, 3], [4, 5, 6, 7]]+      , m [[0, 1, 2, 3], [4, 5, 6, 7]]+      )+    , ( m [[0, 1, 2, 3], [4, 5, 6, 7]]+      , m [[   1, 2, 3], [   5, 6, 7]]+      , m [[0, 1, 2, 3], [4, 5, 6, 7]]+      )+    , ( m [[   1, 2, 3], [   5, 6, 7]]+      , m [[0, 1, 2, 3], [4, 5, 6, 7]]+      , m [[0, 1, 2, 3], [4, 5, 6, 7]]+      )+    , ( m [[0, 1, 2   ], [4, 5, 6   ]]+      , m [[   1, 2, 3], [   5, 6, 7]]+      , m [[0, 1, 2, 3], [4, 5, 6, 7]]+      )+    , ( m [[   1, 2, 3], [   5, 6, 7]]+      , m [[0, 1, 2   ], [4, 5, 6   ]]+      , m [[0, 1, 2, 3], [4, 5, 6, 7]]+      )+    , ( m [[0, 1      ], [4, 5      ]]+      , m [[      2, 3], [      6, 7]]+      , m [[0, 1, 2, 3], [4, 5, 6, 7]]+      )+    , ( m [[      2, 3], [      6, 7]]+      , m [[0, 1      ], [4, 5      ]]+      , m [[0, 1, 2, 3], [4, 5, 6, 7]]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..] . fmap Set.fromList++--------------------------------------------------------------------------------+-- Monus+--------------------------------------------------------------------------------++exampleSpec_Monus_monus_Set_Natural :: Spec+exampleSpec_Monus_monus_Set_Natural = unitTestSpec+    "Monus.monus (Set Natural)"+    "<\\>"+    (<\>)+    (exampleData_Monus_monus_Set_Natural)++exampleData_Monus_monus_Set_Natural :: UnitTestData2+    (MonoidMap LatinChar (Set Natural))+    (MonoidMap LatinChar (Set Natural))+    (MonoidMap LatinChar (Set Natural))+exampleData_Monus_monus_Set_Natural = unitTestData2+    [ ( m [[0, 1, 2], [3, 4, 5]]+      , m [[       ], [       ]]+      , m [[0, 1, 2], [3, 4, 5]]+      )+    , ( m [[0, 1, 2], [3, 4, 5]]+      , m [[0      ], [3      ]]+      , m [[   1, 2], [   4, 5]]+      )+    , ( m [[0, 1, 2], [3, 4, 5]]+      , m [[   1   ], [   4   ]]+      , m [[0,    2], [3,    5]]+      )+    , ( m [[0, 1, 2], [3, 4, 5]]+      , m [[      2], [      5]]+      , m [[0, 1   ], [3, 4   ]]+      )+    , ( m [[0, 1, 2], [3, 4, 5]]+      , m [[0, 1, 2], [3, 4, 5]]+      , m [[       ], [       ]]+      )+    , ( m [[0, 1, 2], [3, 4, 5]]+      , m [[3, 4, 5], [0, 1, 2]]+      , m [[0, 1, 2], [3, 4, 5]]+      )+    , ( m [[0, 1, 2], [3, 4, 5]]+      , m [[2, 3, 4], [1, 2, 3]]+      , m [[0, 1   ], [   4, 5]]+      )+    , ( m [[0, 1, 2], [3, 4, 5]]+      , m [[1, 2, 3], [2, 3, 4]]+      , m [[0      ], [      5]]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..] . fmap Set.fromList++exampleSpec_Monus_monus_Sum_Natural :: Spec+exampleSpec_Monus_monus_Sum_Natural = unitTestSpec+    "Monus.monus (Sum Natural)"+    "<\\>"+    (<\>)+    (exampleData_Monus_monus_Sum_Natural)++exampleData_Monus_monus_Sum_Natural :: UnitTestData2+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+    (MonoidMap LatinChar (Sum Natural))+exampleData_Monus_monus_Sum_Natural = unitTestData2+    [ ( m [0, 1, 2, 3]+      , m [0, 0, 0, 0]+      , m [0, 1, 2, 3]+      )+    , ( m [0, 1, 2, 3]+      , m [1, 1, 1, 1]+      , m [0, 0, 1, 2]+      )+    , ( m [0, 1, 2, 3]+      , m [2, 2, 2, 2]+      , m [0, 0, 0, 1]+      )+    , ( m [0, 1, 2, 3]+      , m [3, 3, 3, 3]+      , m [0, 0, 0, 0]+      )+    , ( m [0, 1, 2, 3]+      , m [4, 4, 4, 4]+      , m [0, 0, 0, 0]+      )+    ]+  where+    m = MonoidMap.fromList . zip [A ..]++--------------------------------------------------------------------------------+-- Utilities+--------------------------------------------------------------------------------++data LatinChar+    = A | B | C | D | E | F | G | H | I | J | K | L | M+    | N | O | P | Q | R | S | T | U | V | W | X | Y | Z+    deriving (Bounded, Enum, Eq, Ord, Show)
+ components/monoidmap-test/Data/MonoidMap/Internal/FilterSpec.hs view
@@ -0,0 +1,163 @@+{- HLINT ignore "Redundant bracket" -}+{- HLINT ignore "Use camelCase" -}+{- HLINT ignore "Use null" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.FilterSpec+    ( spec+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Function+    ( (&) )+import Data.MonoidMap.Internal+    ( MonoidMap, nonNullCount )+import Data.Proxy+    ( Proxy (..) )+import GHC.Exts+    ( IsList (..) )+import Test.Common+    ( Key+    , Test+    , TestValueType (TestValueType)+    , makeSpec+    , property+    , testValueTypesAll+    )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Fun (..), Property, applyFun, applyFun2, cover, (===) )++import qualified Data.List as List+import qualified Data.MonoidMap.Internal as MonoidMap++spec :: Spec+spec = describe "Filtering" $ do++    forM_ testValueTypesAll $+        \(TestValueType p) -> specFor (Proxy @Key) p++specFor :: forall k v. Test k v => Proxy k -> Proxy v -> Spec+specFor = makeSpec $ do++    it "prop_filter_get" $+        prop_filter_get+            @k @v & property+    it "prop_filter_asList" $+        prop_filter_asList+            @k @v & property+    it "prop_filterKeys_get" $+        prop_filterKeys_get+            @k @v & property+    it "prop_filterKeys_asList" $+        prop_filterKeys_asList+            @k @v & property+    it "prop_filterWithKey_get" $+        prop_filterWithKey_get+            @k @v & property+    it "prop_filterWithKey_asList" $+        prop_filterWithKey_asList+            @k @v & property++prop_filter_get+    :: Test k v => Fun v Bool -> k -> MonoidMap k v -> Property+prop_filter_get (applyFun -> f) k m =+    MonoidMap.get k (MonoidMap.filter f m)+        ===+        (MonoidMap.get k m & \v -> if f v then v else mempty)+    & cover 2+        (MonoidMap.nullKey k m && f (MonoidMap.get k m))+        "MonoidMap.nullKey k m && f (MonoidMap.get k m)"+    & cover 2+        (MonoidMap.nullKey k m && not (f (MonoidMap.get k m)))+        "MonoidMap.nullKey k m && not (f (MonoidMap.get k m))"+    & cover 2+        (MonoidMap.nonNullKey k m && f (MonoidMap.get k m))+        "MonoidMap.nonNullKey k m && f (MonoidMap.get k m)"+    & cover 2+        (MonoidMap.nonNullKey k m && not (f (MonoidMap.get k m)))+        "MonoidMap.nonNullKey k m && not (f (MonoidMap.get k m))"++prop_filter_asList+    :: Test k v => Fun v Bool -> MonoidMap k v -> Property+prop_filter_asList (applyFun -> f) m =+    n === fromList (List.filter (f . snd) (toList m))+    & cover 2+        (MonoidMap.nonNull n && nonNullCount n == nonNullCount m)+        "MonoidMap.nonNull n && nonNullCount n == nonNullCount m"+    & cover 2+        (MonoidMap.nonNull n && nonNullCount n /= nonNullCount m)+        "MonoidMap.nonNull n && nonNullCount n /= nonNullCount m"+  where+    n = MonoidMap.filter f m++prop_filterKeys_get+    :: Test k v => Fun k Bool -> k -> MonoidMap k v -> Property+prop_filterKeys_get (applyFun -> f) k m =+    MonoidMap.get k (MonoidMap.filterKeys f m)+        ===+        (if f k then MonoidMap.get k m else mempty)+    & cover 2+        (MonoidMap.nullKey k m && f k)+        "MonoidMap.nullKey k m && f k"+    & cover 2+        (MonoidMap.nullKey k m && not (f k))+        "MonoidMap.nullKey k m && not (f k)"+    & cover 2+        (MonoidMap.nonNullKey k m && f k)+        "MonoidMap.nonNullKey k m && f k"+    & cover 2+        (MonoidMap.nonNullKey k m && not (f k))+        "MonoidMap.nonNullKey k m && not (f k)"++prop_filterKeys_asList+    :: Test k v => Fun k Bool -> MonoidMap k v -> Property+prop_filterKeys_asList (applyFun -> f) m =+    n === MonoidMap.fromList (List.filter (f . fst) (toList m))+    & cover 2+        (MonoidMap.nonNull n && nonNullCount n == nonNullCount m)+        "MonoidMap.nonNull n && nonNullCount n == nonNullCount m"+    & cover 2+        (MonoidMap.nonNull n && nonNullCount n /= nonNullCount m)+        "MonoidMap.nonNull n && nonNullCount n /= nonNullCount m"+  where+    n = MonoidMap.filterKeys f m++prop_filterWithKey_get+    :: Test k v => Fun (k, v) Bool -> k -> MonoidMap k v -> Property+prop_filterWithKey_get (applyFun2 -> f) k m =+    MonoidMap.get k (MonoidMap.filterWithKey f m)+        ===+        (MonoidMap.get k m & \v -> if f k v then v else mempty)+    & cover 2+        (MonoidMap.nullKey k m && f k (MonoidMap.get k m))+        "MonoidMap.nullKey k m && f k (MonoidMap.get k m)"+    & cover 2+        (MonoidMap.nullKey k m && not (f k (MonoidMap.get k m)))+        "MonoidMap.nullKey k m && not (f k (MonoidMap.get k m))"+    & cover 2+        (MonoidMap.nonNullKey k m && f k (MonoidMap.get k m))+        "MonoidMap.nonNullKey k m && f k (MonoidMap.get k m)"+    & cover 2+        (MonoidMap.nonNullKey k m && not (f k (MonoidMap.get k m)))+        "MonoidMap.nonNullKey k m && not (f k (MonoidMap.get k m))"++prop_filterWithKey_asList+    :: Test k v => Fun (k, v) Bool -> MonoidMap k v -> Property+prop_filterWithKey_asList (applyFun2 -> f) m =+    n === MonoidMap.fromList (List.filter (uncurry f) (toList m))+    & cover 2+        (MonoidMap.nonNull n && nonNullCount n == nonNullCount m)+        "MonoidMap.nonNull n && nonNullCount n == nonNullCount m"+    & cover 2+        (MonoidMap.nonNull n && nonNullCount n /= nonNullCount m)+        "MonoidMap.nonNull n && nonNullCount n /= nonNullCount m"+  where+    n = MonoidMap.filterWithKey f m
+ components/monoidmap-test/Data/MonoidMap/Internal/FoldSpec.hs view
@@ -0,0 +1,194 @@+-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.FoldSpec+    ( spec+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Function+    ( (&) )+import Data.MonoidMap.Internal+    ( MonoidMap )+import Data.Proxy+    ( Proxy (..) )+import Test.Common+    ( Key+    , Test+    , TestValueType (TestValueType)+    , makeSpec+    , property+    , testValueTypesAll+    )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Fun (..), Property, applyFun2, applyFun3, (===) )++import qualified Data.Map.Strict as Map+import qualified Data.MonoidMap.Internal as MonoidMap++spec :: Spec+spec = describe "Folding" $ do++    forM_ testValueTypesAll $+        \(TestValueType p) -> specFor (Proxy @Key) p++specFor :: forall k v. Test k v => Proxy k -> Proxy v -> Spec+specFor = makeSpec $ do++    describe "Lazy" $ do++        it "prop_equivalence_foldl" $+            prop_equivalence_foldl+                @k @v & property+        it "prop_equivalence_foldr" $+            prop_equivalence_foldr+                @k @v & property+        it "prop_equivalence_foldlWithKey" $+            prop_equivalence_foldlWithKey+                @k @v & property+        it "prop_equivalence_foldrWithKey" $+            prop_equivalence_foldrWithKey+                @k @v & property+        it "prop_equivalence_foldMapWithKey" $+            prop_equivalence_foldMapWithKey+                @k @v & property++    describe "Strict" $ do++        it "prop_equivalence_foldl'" $+            prop_equivalence_foldl'+                @k @v & property+        it "prop_equivalence_foldr'" $+            prop_equivalence_foldr'+                @k @v & property+        it "prop_equivalence_foldlWithKey'" $+            prop_equivalence_foldlWithKey'+                @k @v & property+        it "prop_equivalence_foldrWithKey'" $+            prop_equivalence_foldrWithKey'+                @k @v & property+        it "prop_equivalence_foldMapWithKey'" $+            prop_equivalence_foldMapWithKey'+                @k @v & property++--------------------------------------------------------------------------------+-- Lazy folding+--------------------------------------------------------------------------------++prop_equivalence_foldl+    :: Test k v+    => r ~ v+    => Fun (r, v) r+    -> r+    -> MonoidMap k v+    -> Property+prop_equivalence_foldl (applyFun2 -> f) r m =+    MonoidMap.foldl f r m+      === Map.foldl f r (MonoidMap.toMap m)++prop_equivalence_foldr+    :: Test k v+    => r ~ v+    => Fun (v, r) r+    -> r+    -> MonoidMap k v+    -> Property+prop_equivalence_foldr (applyFun2 -> f) r m =+    MonoidMap.foldr f r m+      === Map.foldr f r (MonoidMap.toMap m)++prop_equivalence_foldlWithKey+    :: Test k v+    => r ~ v+    => Fun (r, k, v) r+    -> r+    -> MonoidMap k v+    -> Property+prop_equivalence_foldlWithKey (applyFun3 -> f) r m =+    MonoidMap.foldlWithKey f r m+      === Map.foldlWithKey f r (MonoidMap.toMap m)++prop_equivalence_foldrWithKey+    :: Test k v+    => r ~ v+    => Fun (k, v, r) r+    -> r+    -> MonoidMap k v+    -> Property+prop_equivalence_foldrWithKey (applyFun3 -> f) r m =+    MonoidMap.foldrWithKey f r m+      === Map.foldrWithKey f r (MonoidMap.toMap m)++prop_equivalence_foldMapWithKey+    :: Test k v+    => r ~ v+    => Fun (k, v) r+    -> MonoidMap k v+    -> Property+prop_equivalence_foldMapWithKey (applyFun2 -> f) m =+    MonoidMap.foldMapWithKey f m+      === Map.foldMapWithKey f (MonoidMap.toMap m)++--------------------------------------------------------------------------------+-- Strict folding+--------------------------------------------------------------------------------++prop_equivalence_foldl'+    :: Test k v+    => r ~ v+    => Fun (r, v) r+    -> r+    -> MonoidMap k v+    -> Property+prop_equivalence_foldl' (applyFun2 -> f) r m =+    MonoidMap.foldl' f r m ===+    MonoidMap.foldl  f r m++prop_equivalence_foldr'+    :: Test k v+    => r ~ v+    => Fun (v, r) r+    -> r+    -> MonoidMap k v+    -> Property+prop_equivalence_foldr' (applyFun2 -> f) r m =+    MonoidMap.foldr' f r m ===+    MonoidMap.foldr  f r m++prop_equivalence_foldlWithKey'+    :: Test k v+    => r ~ v+    => Fun (r, k, v) r+    -> r+    -> MonoidMap k v+    -> Property+prop_equivalence_foldlWithKey' (applyFun3 -> f) r m =+    MonoidMap.foldlWithKey' f r m ===+    MonoidMap.foldlWithKey  f r m++prop_equivalence_foldrWithKey'+    :: Test k v+    => r ~ v+    => Fun (k, v, r) r+    -> r+    -> MonoidMap k v+    -> Property+prop_equivalence_foldrWithKey' (applyFun3 -> f) r m =+    MonoidMap.foldrWithKey' f r m ===+    MonoidMap.foldrWithKey  f r m++prop_equivalence_foldMapWithKey'+    :: Test k v+    => r ~ v+    => Fun (k, v) r+    -> MonoidMap k v+    -> Property+prop_equivalence_foldMapWithKey' (applyFun2 -> f) m =+    MonoidMap.foldMapWithKey' f m ===+    MonoidMap.foldMapWithKey  f m
+ components/monoidmap-test/Data/MonoidMap/Internal/IntersectionSpec.hs view
@@ -0,0 +1,193 @@+{- HLINT ignore "Redundant bracket" -}+{- HLINT ignore "Use camelCase" -}+{- HLINT ignore "Use null" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.IntersectionSpec+    ( spec+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Function+    ( (&) )+import Data.Functor.Identity+    ( Identity (..) )+import Data.Monoid.Cancellative+    ( GCDMonoid )+import Data.MonoidMap.Internal+    ( MonoidMap )+import Data.Proxy+    ( Proxy (..) )+import Test.Common+    ( Key+    , Test+    , TestValueType (TestValueType)+    , makeSpec+    , property+    , testValueTypesGCDMonoid+    , testValueTypesAll+    )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Fun (..), Property, applyFun2, conjoin, cover, expectFailure, (===) )++import qualified Data.Monoid.Null as Null+import qualified Data.MonoidMap.Internal as MonoidMap+import qualified Data.Set as Set++spec :: Spec+spec = describe "Intersection" $ do++    forM_ testValueTypesAll $+        \(TestValueType p) -> specMonoidNull+            (Proxy @Key) p+    forM_ testValueTypesGCDMonoid $+        \(TestValueType p) -> specGCDMonoid+            (Proxy @Key) p++specMonoidNull :: forall k v. Test k v => Proxy k -> Proxy v -> Spec+specMonoidNull = makeSpec $ do+    it "prop_intersectionWith_get" $+        prop_intersectionWith_get+            @k @v & property+    it "prop_intersectionWith_get_total" $+        prop_intersectionWith_get_total+            @k @v & property+    it "prop_intersectionWith_get_total_failure" $+        prop_intersectionWith_get_total_failure+            @k @v & property+    it "prop_intersectionWith_intersectionWithA" $+        prop_intersectionWith_intersectionWithA+            @k @v & property++specGCDMonoid+    :: forall k v. (Test k v, GCDMonoid v) => Proxy k -> Proxy v -> Spec+specGCDMonoid = makeSpec $ do+    it "prop_intersection_isSubmapOf" $+        prop_intersection_isSubmapOf+            @k @v & property++prop_intersection_isSubmapOf+    :: (Test k v, GCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+prop_intersection_isSubmapOf m1 m2 = conjoin+    [ intersection_m1_m2 `MonoidMap.isSubmapOf` m1+    , intersection_m1_m2 `MonoidMap.isSubmapOf` m2+    ]+    & cover 2+        (m1 /= m2 && MonoidMap.nonNull (intersection_m1_m2))+        "m1 /= m2 && MonoidMap.nonNull (intersection_m1_m2)"+  where+    intersection_m1_m2 = MonoidMap.intersection m1 m2++prop_intersectionWith_get+    :: Test k v+    => Fun (v, v) v+    -> MonoidMap k v+    -> MonoidMap k v+    -> k+    -> Property+prop_intersectionWith_get (applyFun2 -> f) m1 m2 k =+    (MonoidMap.get k result+        ===+        if keyWithinIntersection+        then f (MonoidMap.get k m1) (MonoidMap.get k m2)+        else mempty)+    & cover 2+        (keyWithinIntersection)+        "keyWithinIntersection"+    & cover 2+        (not keyWithinIntersection)+        "not keyWithinIntersection"+    & cover 2+        (MonoidMap.null result)+        "MonoidMap.null result"+    & cover 2+        (MonoidMap.nonNull result)+        "MonoidMap.nonNull result"+    & cover 2+        (MonoidMap.nullKey k result)+        "MonoidMap.nullKey k result"+    & cover 2+        (MonoidMap.nonNullKey k result)+        "MonoidMap.nonNullKey k result"+  where+    keyWithinIntersection =+        k `Set.member` Set.intersection+            (MonoidMap.nonNullKeys m1)+            (MonoidMap.nonNullKeys m2)+    result =+        MonoidMap.intersectionWith f m1 m2++prop_intersectionWith_get_total+    :: Test k v+    => Fun (v, v) v+    -> MonoidMap k v+    -> MonoidMap k v+    -> k+    -> Property+prop_intersectionWith_get_total (applyFun2 -> f0) m1 m2 k =+    (MonoidMap.get k result+        ===+        f (MonoidMap.get k m1) (MonoidMap.get k m2))+    & cover 2+        (keyWithinIntersection)+        "keyWithinIntersection"+    & cover 2+        (not keyWithinIntersection)+        "not keyWithinIntersection"+    & cover 2+        (MonoidMap.null result)+        "MonoidMap.null result"+    & cover 2+        (MonoidMap.nonNull result)+        "MonoidMap.nonNull result"+    & cover 2+        (MonoidMap.nullKey k result)+        "MonoidMap.nullKey k result"+    & cover 2+        (MonoidMap.nonNullKey k result)+        "MonoidMap.nonNullKey k result"+  where+    result =+        MonoidMap.intersectionWith f m1 m2+    keyWithinIntersection =+        k `Set.member` Set.intersection+            (MonoidMap.nonNullKeys m1)+            (MonoidMap.nonNullKeys m2)+    f v1 v2+        | Null.null v1 = mempty+        | Null.null v2 = mempty+        | otherwise = f0 v1 v2++prop_intersectionWith_get_total_failure+    :: Test k v+    => Fun (v, v) v+    -> MonoidMap k v+    -> MonoidMap k v+    -> k+    -> Property+prop_intersectionWith_get_total_failure (applyFun2 -> f) m1 m2 k =+    expectFailure $+    MonoidMap.get k (MonoidMap.intersectionWith f m1 m2)+        ===+        f (MonoidMap.get k m1) (MonoidMap.get k m2)++prop_intersectionWith_intersectionWithA+    :: Test k v+    => Fun (v, v) v+    -> MonoidMap k v+    -> MonoidMap k v+    -> Property+prop_intersectionWith_intersectionWithA (applyFun2 -> f) m1 m2 =+    runIdentity (MonoidMap.intersectionWithA ((fmap . fmap) Identity f) m1 m2)+    ===         (MonoidMap.intersectionWith                          f  m1 m2)
+ components/monoidmap-test/Data/MonoidMap/Internal/MapSpec.hs view
@@ -0,0 +1,300 @@+{- HLINT ignore "Redundant bracket" -}+{- HLINT ignore "Use camelCase" -}+{- HLINT ignore "Use null" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.MapSpec+    ( spec+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Bifunctor+    ( first, second )+import Data.Function+    ( (&) )+import Data.Monoid.Null+    ( MonoidNull )+import Data.MonoidMap.Internal+    ( MonoidMap, nonNullCount )+import Data.Proxy+    ( Proxy (..) )+import Test.Common+    ( Key+    , Test+    , TestValueType (TestValueType)+    , makeSpec+    , property+    , testValueTypesAll+    )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Fun (..), Property, applyFun, applyFun2, cover, expectFailure, (===) )++import qualified Data.Foldable as F+import qualified Data.Monoid.Null as Null+import qualified Data.MonoidMap.Internal as MonoidMap+import qualified Data.Set as Set++spec :: Spec+spec = describe "Mapping" $ do++    forM_ testValueTypesAll $+        \(TestValueType p) -> specFor (Proxy @Key) p++specFor :: forall k v. Test k v => Proxy k -> Proxy v -> Spec+specFor = makeSpec $ do++    it "prop_map_asList" $+        prop_map_asList+            @k @v & property+    it "prop_map_composition" $+        prop_map_composition+            @k @v & property+    it "prop_map_composition_failure" $+        prop_map_composition_failure+            @k @v & property+    it "prop_map_get" $+        prop_map_get+            @k @v & property+    it "prop_map_get_total" $+        prop_map_get_total+            @k @v & property+    it "prop_map_get_total_failure" $+        prop_map_get_total_failure+            @k @v & property+    it "prop_mapKeys_asList" $+        prop_mapKeys_asList+            @k @v & property+    it "prop_mapKeys_get" $+        prop_mapKeys_get+            @k @v & property+    it "prop_mapKeysWith_asList" $+        prop_mapKeysWith_asList+            @k @v & property+    it "prop_mapWithKey_asList" $+        prop_mapWithKey_asList+            @k @v & property+    it "prop_mapWithKey_get" $+        prop_mapWithKey_get+            @k @v & property+    it "prop_mapWithKey_get_total" $+        prop_mapWithKey_get_total+            @k @v & property+    it "prop_mapWithKey_get_total_failure" $+        prop_mapWithKey_get_total_failure+            @k @v & property++--------------------------------------------------------------------------------+-- Mapping+--------------------------------------------------------------------------------++prop_map_asList+    :: Test k v+    => Fun v v+    -> MonoidMap k v+    -> Property+prop_map_asList (applyFun -> f) m =+    n === (MonoidMap.fromList . fmap (second f) . MonoidMap.toList $ m)+    & cover 2+        (0 < nonNullCount n && nonNullCount n < nonNullCount m)+        "0 < nonNullCount n && nonNullCount n < nonNullCount m"+  where+    n = MonoidMap.map f m++prop_map_composition+    :: forall k v. Test k v+    => Fun v v+    -> Fun v v+    -> MonoidMap k v+    -> Property+prop_map_composition (applyFun -> f0) (applyFun -> g0) m =+    MonoidMap.map (f . g) m === MonoidMap.map f (MonoidMap.map g m)+    & cover 2+        (MonoidMap.nonNull m)+        "MonoidMap.nonNull m"+  where+    f = toNullPreservingFn f0+    g = g0++prop_map_composition_failure+    :: forall k v. Test k v+    => Fun v v+    -> Fun v v+    -> MonoidMap k v+    -> Property+prop_map_composition_failure (applyFun -> f) (applyFun -> g) m =+    expectFailure $+    MonoidMap.map (f . g) m === MonoidMap.map f (MonoidMap.map g m)+    & cover 1+        (MonoidMap.map (f . g) m /= MonoidMap.map f (MonoidMap.map g m))+        "MonoidMap.map (f . g) m /= MonoidMap.map f (MonoidMap.map g m)"++prop_map_get+    :: Test k v+    => Fun v v+    -> k+    -> MonoidMap k v+    -> Property+prop_map_get (applyFun -> f) k m =+    MonoidMap.get k (MonoidMap.map f m)+    ===+    (if MonoidMap.nullKey k m then mempty else f (MonoidMap.get k m))+    & cover 2+        (MonoidMap.nullKey k m)+        "MonoidMap.nullKey k m"+    & cover 2+        (MonoidMap.nonNullKey k m)+        "MonoidMap.nonNullKey k m"++prop_map_get_total+    :: forall k v. Test k v+    => Fun v v+    -> k+    -> MonoidMap k v+    -> Property+prop_map_get_total (applyFun -> f0) k m =+    MonoidMap.get k (MonoidMap.map f m) === f (MonoidMap.get k m)+    & cover 2+        (MonoidMap.nullKey k m)+        "MonoidMap.nullKey k m"+    & cover 2+        (MonoidMap.nonNullKey k m)+        "MonoidMap.nonNullKey k m"+  where+    f = toNullPreservingFn f0++prop_map_get_total_failure+    :: Test k v+    => Fun v v+    -> k+    -> MonoidMap k v+    -> Property+prop_map_get_total_failure (applyFun -> f) k m =+    expectFailure $+    MonoidMap.get k (MonoidMap.map f m) === f (MonoidMap.get k m)++prop_mapKeys_asList+    :: Test k v+    => Fun k k+    -> MonoidMap k v+    -> Property+prop_mapKeys_asList (applyFun -> f) m =+    n === (MonoidMap.fromList . fmap (first f) . MonoidMap.toList $ m)+    & cover 2+        (0 < nonNullCount n && nonNullCount n < nonNullCount m)+        "0 < nonNullCount n && nonNullCount n < nonNullCount m"+  where+    n = MonoidMap.mapKeys f m++prop_mapKeys_get+    :: Test k v+    => Fun k k+    -> k+    -> MonoidMap k v+    -> Property+prop_mapKeys_get (applyFun -> f) k m =+    MonoidMap.get k (MonoidMap.mapKeys f m)+        ===+        F.foldMap+            (`MonoidMap.get` m)+            (Set.filter ((==) k . f) (MonoidMap.nonNullKeys m))+    & cover 2+        (MonoidMap.nullKey k (MonoidMap.mapKeys f m))+        "MonoidMap.nullKey k (MonoidMap.mapKeys f m)"+    & cover 2+        (MonoidMap.nonNullKey k (MonoidMap.mapKeys f m))+        "MonoidMap.nonNullKey k (MonoidMap.mapKeys f m)"++prop_mapKeysWith_asList+    :: Test k v+    => Fun (v, v) v+    -> Fun k k+    -> MonoidMap k v+    -> Property+prop_mapKeysWith_asList (applyFun2 -> c) (applyFun -> f) m =+    n === (MonoidMap.fromListWith c . fmap (first f) . MonoidMap.toList $ m)+    & cover 2+        (0 < nonNullCount n && nonNullCount n < nonNullCount m)+        "0 < nonNullCount n && nonNullCount n < nonNullCount m"+  where+    n = MonoidMap.mapKeysWith c f m++prop_mapWithKey_asList+    :: Test k v+    => Fun (k, v) v+    -> MonoidMap k v+    -> Property+prop_mapWithKey_asList (applyFun2 -> f) m =+    n ===+        ( MonoidMap.fromList+        . fmap (\(k, v) -> (k, (f k v)))+        . MonoidMap.toList+        $ m+        )+    & cover 2+        (0 < nonNullCount n && nonNullCount n < nonNullCount m)+        "0 < nonNullCount n && nonNullCount n < nonNullCount m"+  where+    n = MonoidMap.mapWithKey f m++prop_mapWithKey_get+    :: Test k v+    => Fun (k, v) v+    -> k+    -> MonoidMap k v+    -> Property+prop_mapWithKey_get (applyFun2 -> f) k m =+    MonoidMap.get k (MonoidMap.mapWithKey f m)+    ===+    (if MonoidMap.nullKey k m then mempty else f k (MonoidMap.get k m))+    & cover 2+        (MonoidMap.nullKey k m)+        "MonoidMap.nullKey k m"+    & cover 2+        (MonoidMap.nonNullKey k m)+        "MonoidMap.nonNullKey k m"++prop_mapWithKey_get_total+    :: forall k v. Test k v+    => Fun (k, v) v+    -> k+    -> MonoidMap k v+    -> Property+prop_mapWithKey_get_total (applyFun2 -> f0) k m =+    MonoidMap.get k (MonoidMap.mapWithKey f m) === f k (MonoidMap.get k m)+    & cover 2+        (MonoidMap.nullKey k m)+        "MonoidMap.nullKey k m"+    & cover 2+        (MonoidMap.nonNullKey k m)+        "MonoidMap.nonNullKey k m"+  where+    f = toNullPreservingFn . f0++prop_mapWithKey_get_total_failure+    :: Test k v+    => Fun (k, v) v+    -> k+    -> MonoidMap k v+    -> Property+prop_mapWithKey_get_total_failure (applyFun2 -> f) k m =+    expectFailure $+    MonoidMap.get k (MonoidMap.mapWithKey f m) === f k (MonoidMap.get k m)++--------------------------------------------------------------------------------+-- Utilities+--------------------------------------------------------------------------------++-- | Creates a function that never maps null values to non-null values.+--+toNullPreservingFn :: MonoidNull v => (v -> v) -> (v -> v)+toNullPreservingFn f v+    | Null.null v = v+    | otherwise = f v
+ components/monoidmap-test/Data/MonoidMap/Internal/MembershipSpec.hs view
@@ -0,0 +1,106 @@+{- HLINT ignore "Redundant bracket" -}+{- HLINT ignore "Use camelCase" -}+{- HLINT ignore "Use null" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.MembershipSpec+    ( spec+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Function+    ( (&) )+import Data.MonoidMap.Internal+    ( MonoidMap )+import Data.Proxy+    ( Proxy (..) )+import Test.Common+    ( Key+    , Test+    , TestValueType (TestValueType)+    , makeSpec+    , property+    , testValueTypesAll+    )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Property, cover, (===) )++import qualified Data.MonoidMap.Internal as MonoidMap+import qualified Data.Set as Set++spec :: Spec+spec = describe "Membership" $ do++    forM_ testValueTypesAll $+        \(TestValueType p) -> specFor (Proxy @Key) p++specFor :: forall k v. Test k v => Proxy k -> Proxy v -> Spec+specFor = makeSpec $ do++    it "prop_nullify_get" $+        prop_nullify_get+            @k @v & property+    it "prop_nullify_nonNullKey" $+        prop_nullify_nonNullKey+            @k @v & property+    it "prop_nullify_nonNullKeys" $+        prop_nullify_nonNullKeys+            @k @v & property+    it "prop_nonNullKeys_get" $+        prop_nonNullKeys_get+            @k @v & property++prop_nullify_get+    :: Test k v => MonoidMap k v -> k -> Property+prop_nullify_get m k =+    MonoidMap.get k (MonoidMap.nullify k m) === mempty+    & cover 2+        (MonoidMap.nonNullKey k m)+        "MonoidMap.nonNullKey k m"+    & cover 2+        (not (MonoidMap.nonNullKey k m))+        "not (MonoidMap.nonNullKey k m)"++prop_nullify_nonNullKey+    :: Test k v => MonoidMap k v -> k -> Property+prop_nullify_nonNullKey m k =+    MonoidMap.nonNullKey k (MonoidMap.nullify k m) === False+    & cover 2+        (MonoidMap.nonNullKey k m)+        "MonoidMap.nonNullKey k m"+    & cover 2+        (not (MonoidMap.nonNullKey k m))+        "not (MonoidMap.nonNullKey k m)"++prop_nullify_nonNullKeys+    :: Test k v => MonoidMap k v -> k -> Property+prop_nullify_nonNullKeys m k =+    Set.member k (MonoidMap.nonNullKeys (MonoidMap.nullify k m)) === False+    & cover 2+        (MonoidMap.nonNullKey k m)+        "MonoidMap.nonNullKey k m"+    & cover 2+        (not (MonoidMap.nonNullKey k m))+        "not (MonoidMap.nonNullKey k m)"++prop_nonNullKeys_get+    :: Test k v => MonoidMap k v -> Property+prop_nonNullKeys_get m =+    fmap+        (\k -> (k, MonoidMap.get k m))+        (Set.toList (MonoidMap.nonNullKeys m))+        === MonoidMap.toList m+    & cover 2+        (MonoidMap.null m)+        "MonoidMap.null m"+    & cover 2+        (not (MonoidMap.null m))+        "not (MonoidMap.null m)"
+ components/monoidmap-test/Data/MonoidMap/Internal/PartitionSpec.hs view
@@ -0,0 +1,173 @@+{- HLINT ignore "Redundant bracket" -}+{- HLINT ignore "Use camelCase" -}+{- HLINT ignore "Use null" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.PartitionSpec+    ( spec+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Function+    ( (&) )+import Data.MonoidMap.Internal+    ( MonoidMap )+import Data.Proxy+    ( Proxy (..) )+import Test.Common+    ( Key+    , Test+    , TestValueType (TestValueType)+    , makeSpec+    , property+    , testValueTypesAll+    )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Fun (..), Property, applyFun, applyFun2, cover, (===) )++import qualified Data.MonoidMap.Internal as MonoidMap+import qualified Data.Set as Set++spec :: Spec+spec = describe "Partitioning" $ do++    forM_ testValueTypesAll $+        \(TestValueType p) -> specFor (Proxy @Key) p++specFor :: forall k v. Test k v => Proxy k -> Proxy v -> Spec+specFor = makeSpec $ do++    it "prop_partition_filter" $+        prop_partition_filter+            @k @v & property+    it "prop_partition_append" $+        prop_partition_append+            @k @v & property+    it "prop_partition_disjoint" $+        prop_partition_disjoint+            @k @v & property+    it "prop_partitionKeys_filterKeys" $+        prop_partitionKeys_filterKeys+            @k @v & property+    it "prop_partitionKeys_append" $+        prop_partitionKeys_append+            @k @v & property+    it "prop_partitionKeys_disjoint" $+        prop_partitionKeys_disjoint+            @k @v & property+    it "prop_partitionWithKey_filterWithKey" $+        prop_partitionWithKey_filterWithKey+            @k @v & property+    it "prop_partitionWithKey_append" $+        prop_partitionWithKey_append+            @k @v & property+    it "prop_partitionWithKey_disjoint" $+        prop_partitionWithKey_disjoint+            @k @v & property++prop_partition_filter+    :: Test k v => Fun v Bool -> MonoidMap k v -> Property+prop_partition_filter (applyFun -> f) m =+    MonoidMap.partition f m === (m1, m2)+    & cover 2+        (MonoidMap.nonNull m1 && MonoidMap.nonNull m2)+        "MonoidMap.nonNull m1 && MonoidMap.nonNull m2"+  where+    m1 = MonoidMap.filter f m+    m2 = MonoidMap.filter (not . f) m++prop_partition_append+    :: Test k v => Fun v Bool -> MonoidMap k v -> Property+prop_partition_append (applyFun -> f) m =+    m1 <> m2 === m+    & cover 2+        (MonoidMap.nonNull m1 && MonoidMap.nonNull m2)+        "MonoidMap.nonNull m1 && MonoidMap.nonNull m2"+  where+    (m1, m2) = MonoidMap.partition f m++prop_partition_disjoint+    :: Test k v => Fun v Bool -> MonoidMap k v -> Property+prop_partition_disjoint (applyFun -> f) m =+    Set.disjoint+        (MonoidMap.nonNullKeys m1)+        (MonoidMap.nonNullKeys m2)+    & cover 2+        (MonoidMap.nonNull m1 && MonoidMap.nonNull m2)+        "MonoidMap.nonNull m1 && MonoidMap.nonNull m2"+  where+    (m1, m2) = MonoidMap.partition f m++prop_partitionKeys_filterKeys+    :: Test k v => Fun k Bool -> MonoidMap k v -> Property+prop_partitionKeys_filterKeys (applyFun -> f) m =+    MonoidMap.partitionKeys f m === (m1, m2)+    & cover 2+        (MonoidMap.nonNull m1 && MonoidMap.nonNull m2)+        "MonoidMap.nonNull m1 && MonoidMap.nonNull m2"+  where+    m1 = MonoidMap.filterKeys f m+    m2 = MonoidMap.filterKeys (not . f) m++prop_partitionKeys_append+    :: Test k v => Fun k Bool -> MonoidMap k v -> Property+prop_partitionKeys_append (applyFun -> f) m =+    m1 <> m2 === m+    & cover 2+        (MonoidMap.nonNull m1 && MonoidMap.nonNull m2)+        "MonoidMap.nonNull m1 && MonoidMap.nonNull m2"+  where+    (m1, m2) = MonoidMap.partitionKeys f m++prop_partitionKeys_disjoint+    :: Test k v => Fun k Bool -> MonoidMap k v -> Property+prop_partitionKeys_disjoint (applyFun -> f) m =+    Set.disjoint+        (MonoidMap.nonNullKeys m1)+        (MonoidMap.nonNullKeys m2)+    & cover 2+        (MonoidMap.nonNull m1 && MonoidMap.nonNull m2)+        "MonoidMap.nonNull m1 && MonoidMap.nonNull m2"+  where+    (m1, m2) = MonoidMap.partitionKeys f m++prop_partitionWithKey_filterWithKey+    :: Test k v => Fun (k, v) Bool -> MonoidMap k v -> Property+prop_partitionWithKey_filterWithKey (applyFun2 -> f) m =+    MonoidMap.partitionWithKey f m === (m1, m2)+    & cover 2+        (MonoidMap.nonNull m1 && MonoidMap.nonNull m2)+        "MonoidMap.nonNull m1 && MonoidMap.nonNull m2"+  where+    m1 = MonoidMap.filterWithKey f m+    m2 = MonoidMap.filterWithKey ((fmap . fmap) not f) m++prop_partitionWithKey_append+    :: Test k v => Fun (k, v) Bool -> MonoidMap k v -> Property+prop_partitionWithKey_append (applyFun2 -> f) m =+    m1 <> m2 === m+    & cover 2+        (MonoidMap.nonNull m1 && MonoidMap.nonNull m2)+        "MonoidMap.nonNull m1 && MonoidMap.nonNull m2"+  where+    (m1, m2) = MonoidMap.partitionWithKey f m++prop_partitionWithKey_disjoint+    :: Test k v => Fun (k, v) Bool -> MonoidMap k v -> Property+prop_partitionWithKey_disjoint (applyFun2 -> f) m =+    Set.disjoint+        (MonoidMap.nonNullKeys m1)+        (MonoidMap.nonNullKeys m2)+    & cover 2+        (MonoidMap.nonNull m1 && MonoidMap.nonNull m2)+        "MonoidMap.nonNull m1 && MonoidMap.nonNull m2"+  where+    (m1, m2) = MonoidMap.partitionWithKey f m
+ components/monoidmap-test/Data/MonoidMap/Internal/PrefixSpec.hs view
@@ -0,0 +1,80 @@+{- HLINT ignore "Redundant bracket" -}+{- HLINT ignore "Use camelCase" -}+{- HLINT ignore "Use null" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.PrefixSpec+    ( spec+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Function+    ( (&) )+import Data.Maybe+    ( isJust )+import Data.MonoidMap.Internal+    ( MonoidMap )+import Data.Proxy+    ( Proxy (..) )+import Data.Semigroup.Cancellative+    ( LeftReductive (..) )+import Test.Common+    ( Key+    , Test+    , TestValueType (TestValueType)+    , makeSpec+    , property+    , testValueTypesLeftReductive+    )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Property, cover, (===) )++import qualified Test.QuickCheck as QC++spec :: Spec+spec = describe "Prefixes" $ do++    forM_ testValueTypesLeftReductive $+        \(TestValueType p) -> specFor (Proxy @Key) p++specFor+    :: forall k v. (Test k v, LeftReductive v) => Proxy k -> Proxy v -> Spec+specFor = makeSpec $ do+    it "prop_stripPrefix_isJust" $+        prop_stripPrefix_isJust+            @k @v & property+    it "prop_stripPrefix_mappend" $+        prop_stripPrefix_mappend+            @k @v & property++prop_stripPrefix_isJust+    :: (Test k v, LeftReductive v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+prop_stripPrefix_isJust m1 m2 =+    isJust (stripPrefix m1 m2) === m1 `isPrefixOf` m2+    & cover 1+        (m1 `isPrefixOf` m2)+        "m1 `isPrefixOf` m2"++prop_stripPrefix_mappend+    :: (Test k v, LeftReductive v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+prop_stripPrefix_mappend m1 m2 = QC.property $+    all+        (\r -> m1 <> r == m2)+        (stripPrefix m1 m2)+    & cover 1+        (isJust (stripPrefix m1 m2))+        "isJust (stripPrefix m1 m2)"
+ components/monoidmap-test/Data/MonoidMap/Internal/RecoveredMapSpec.hs view
@@ -0,0 +1,584 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}+{-# HLINT ignore "Use any" #-}+{-# HLINT ignore "Use null" #-}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.RecoveredMapSpec+    where++import Prelude++import Data.Function+    ( on, (&) )+import Data.List+    ( nubBy )+import Data.Monoid+    ( Sum (..) )+import Data.Proxy+    ( Proxy (..) )+import Data.Semigroup+    ( Semigroup (stimes) )+import Data.Set+    ( Set )+import Data.Text+    ( Text )+import Data.Typeable+    ( Typeable, typeRep )+import Numeric.Natural+    ( Natural )+import Test.Common+    ()+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Arbitrary (..)+    , CoArbitrary+    , Fun+    , Function+    , NonNegative (..)+    , Property+    , Testable+    , applyFun+    , applyFun2+    , applyFun3+    , checkCoverage+    , cover+    , listOf+    , shrinkMapBy+    , (===)+    )+import Test.QuickCheck.Classes+    ( eqLaws, functorLaws, monoidLaws, semigroupLaws, semigroupMonoidLaws )+import Test.QuickCheck.Classes.Hspec+    ( testLawsMany )++import qualified Data.Map.Strict as OMap+import qualified Data.Set as Set+import qualified Data.MonoidMap.Internal.RecoveredMap as RMap+import qualified Test.QuickCheck as QC++spec :: Spec+spec = do+    specFor (Proxy @Int) (Proxy @(Set Int))+    specFor (Proxy @Int) (Proxy @(Set Natural))+    specFor (Proxy @Int) (Proxy @(Sum Int))+    specFor (Proxy @Int) (Proxy @(Sum Natural))+    specFor (Proxy @Int) (Proxy @Text)++specFor+    :: forall k v. () =>+        ( Arbitrary k+        , Arbitrary v+        , CoArbitrary k+        , CoArbitrary v+        , Eq v+        , Function k+        , Function v+        , Monoid v+        , Ord k+        , Show k+        , Show v+        , Typeable k+        , Typeable v+        )+    => Proxy k+    -> Proxy v+    -> Spec+specFor keyType valueType = do++    let description = mconcat+            [ "RecoveredMap ("+            , show (typeRep keyType)+            , ") ("+            , show (typeRep valueType)+            , ")"+            ]++    let property :: Testable t => t -> Property+        property = checkCoverage . QC.property++    describe description $ do++        describe "Class laws" $ do+            testLawsMany @(RMap.Map k v)+                [ eqLaws+                , monoidLaws+                , semigroupLaws+                , semigroupMonoidLaws+                ]+            testLawsMany @(RMap.Map k)+                [ functorLaws+                ]++        describe "Conversion to and from lists" $ do+            it "prop_fromList_toList" $+                prop_fromList_toList+                    @k @v & property++        describe "Empty" $ do+            it "prop_empty_keysSet" $+                prop_empty_keysSet+                    @k & property+            it "prop_empty_lookup" $+                prop_empty_lookup+                    @k @v & property+            it "prop_empty_show" $+                prop_empty_show+                    @k @v & property+            it "prop_empty_toList" $+                prop_empty_toList+                    @k @v & property++        describe "Singleton" $ do+            it "prop_singleton_keysSet" $+                prop_singleton_keysSet+                    @k @v & property+            it "prop_singleton_lookup" $+                prop_singleton_lookup+                    @k @v & property+            it "prop_singleton_show" $+                prop_singleton_show+                    @k @v & property+            it "prop_singleton_toList" $+                prop_singleton_toList+                    @k @v & property++        describe "Append" $ do+            it "prop_append_toList" $+                prop_append_toList+                    @k @v & property++        describe "Times" $ do+            it "prop_stimes_toList" $+                prop_stimes_toList+                    @k @v & property++        describe "Delete" $ do+            it "prop_delete_lookup" $+                prop_delete_lookup+                    @k @v & property+            it "prop_delete_member" $+                prop_delete_member+                    @k @v & property+            it "prop_delete_toList" $+                prop_delete_toList+                    @k @v & property++        describe "Insert" $ do+            it "prop_insert_lookup" $+                prop_insert_lookup+                    @k @v & property+            it "prop_insert_member" $+                prop_insert_member+                    @k @v & property+            it "prop_insert_toList" $+                prop_insert_toList+                    @k @v & property++        describe "Map" $ do+            it "prop_map" $+                prop_map+                    @k @v @v & property+            it "prop_map_mempty" $+                prop_map_mempty+                    @k @v @v & property+            it "prop_mapWithKey" $+                prop_mapWithKey+                    @k @v @v & property++        describe "MapAccumL" $ do+            it "prop_mapAccumL @Int" $+                prop_mapAccumL @Int+                    @k @v @v & property+            it "prop_mapAccumL @String" $+                prop_mapAccumL @String+                    @k @v @v & property++        describe "MapAccumR" $ do+            it "prop_mapAccumR @Int" $+                prop_mapAccumR @Int+                    @k @v @v & property+            it "prop_mapAccumR @String" $+                prop_mapAccumR @String+                    @k @v @v & property++        describe "MapAccumWithKeyL" $ do+            it "prop_mapAccumLWithKey @Int" $+                prop_mapAccumLWithKey @Int+                    @k @v @v & property+            it "prop_mapAccumLWithKey @String" $+                prop_mapAccumLWithKey @String+                    @k @v @v & property++        describe "MapAccumWithKeyR" $ do+            it "prop_mapAccumRWithKey @Int" $+                prop_mapAccumRWithKey @Int+                    @k @v @v & property+            it "prop_mapAccumRWithKey @String" $+                prop_mapAccumRWithKey @String+                    @k @v @v & property++--------------------------------------------------------------------------------+-- Conversion to and from lists+--------------------------------------------------------------------------------++prop_fromList_toList+    :: forall k v. (Ord k, Show k, Eq v, Show v)+    => [(k, v)]+    -> Property+prop_fromList_toList kvs =+    (===)+        (RMap.toList (RMap.fromList kvs))+        (OMap.toList (OMap.fromList kvs))+    & cover 10+        (length kvs > 1 && length (nubBy ((==) `on` fst) kvs) /= length kvs)+        "length kvs > 1 && length (nubBy ((==) `on` fst) kvs) /= length kvs"+    & cover 10+        (length kvs > 1 && length (nubBy ((==) `on` fst) kvs) == length kvs)+        "length kvs > 1 && length (nubBy ((==) `on` fst) kvs) == length kvs"++--------------------------------------------------------------------------------+-- Empty+--------------------------------------------------------------------------------++prop_empty_keysSet+    :: forall k. (Eq k, Show k)+    => Property+prop_empty_keysSet =+    (===)+        (RMap.keysSet (RMap.empty @k))+        (OMap.keysSet (OMap.empty @k))++prop_empty_lookup+    :: forall k v. (Ord k, Eq v, Show v)+    => k+    -> Property+prop_empty_lookup k =+    (===)+        (RMap.lookup k (RMap.empty @k @v))+        (OMap.lookup k (OMap.empty @k @v))++prop_empty_show+    :: forall k v. (Show k, Show v)+    => Property+prop_empty_show =+    (===)+        (show (RMap.empty @k @v))+        (show (OMap.empty @k @v))++prop_empty_toList+    :: forall k v. (Eq k, Show k, Eq v, Show v)+    => Property+prop_empty_toList =+    (===)+        (RMap.toList (RMap.empty @k @v))+        (OMap.toList (OMap.empty @k @v))++--------------------------------------------------------------------------------+-- Singleton+--------------------------------------------------------------------------------++prop_singleton_keysSet+    :: forall k v. (Ord k, Show k)+    => k+    -> v+    -> Property+prop_singleton_keysSet k v =+    (===)+        (RMap.keysSet (RMap.singleton k v))+        (OMap.keysSet (OMap.singleton k v))++prop_singleton_lookup+    :: forall k v. (Ord k, Eq v, Show v)+    => k+    -> v+    -> Property+prop_singleton_lookup k v =+    (===)+        (RMap.lookup k (RMap.singleton k v))+        (OMap.lookup k (OMap.singleton k v))++prop_singleton_show+    :: forall k v. (Ord k, Show k, Show v)+    => k+    -> v+    -> Property+prop_singleton_show k v =+    (===)+        (show (RMap.singleton k v))+        (show (OMap.singleton k v))++prop_singleton_toList+    :: forall k v. (Ord k, Show k, Eq v, Show v)+    => k+    -> v+    -> Property+prop_singleton_toList k v =+    (===)+        (RMap.toList (RMap.singleton k v))+        (OMap.toList (OMap.singleton k v))++--------------------------------------------------------------------------------+-- Append+--------------------------------------------------------------------------------++prop_append_toList+    :: forall k v. (Ord k, Show k, Eq v, Show v)+    => [(k, v)]+    -> [(k, v)]+    -> Property+prop_append_toList kvs1 kvs2 =+    (===)+        (RMap.toList (RMap.fromList kvs1 <> RMap.fromList kvs2))+        (OMap.toList (OMap.fromList kvs1 <> OMap.fromList kvs2))+    & cover 10+        (ks1 `Set.disjoint` ks2)+        "ks1 `Set.disjoint` ks2"+    & cover 10+        (not (ks1 `Set.disjoint` ks2))+        "not (ks1 `Set.disjoint` ks2)"+  where+    ks1 = Set.fromList (fst <$> kvs1)+    ks2 = Set.fromList (fst <$> kvs2)++--------------------------------------------------------------------------------+-- Times+--------------------------------------------------------------------------------++prop_stimes_toList+    :: forall k v. (Ord k, Show k, Eq v, Show v)+    => [(k, v)]+    -> NonNegative Int+    -> Property+prop_stimes_toList kvs (NonNegative n) =+    (===)+        (RMap.toList (stimes n (RMap.fromList kvs)))+        (OMap.toList (stimes n (OMap.fromList kvs)))+    & cover 1+        (n == 0)+        "n == 0"+    & cover 1+        (n == 1)+        "n == 1"+    & cover 10+        (n >= 2)+        "n >= 2"++--------------------------------------------------------------------------------+-- Delete+--------------------------------------------------------------------------------++prop_delete_lookup+    :: forall k v. (Ord k, Eq v, Show v)+    => [(k, v)]+    -> k+    -> Property+prop_delete_lookup kvs k =+    (===)+        (RMap.lookup k (RMap.delete k (RMap.fromList kvs)))+        (OMap.lookup k (OMap.delete k (OMap.fromList kvs)))+    & cover 10+        (filter ((== k) . fst) kvs == [])+        "filter ((== k) . fst) kvs == []"+    & cover 10+        (filter ((== k) . fst) kvs /= [])+        "filter ((== k) . fst) kvs /= []"++prop_delete_member+    :: forall k v. (Ord k, Eq v)+    => [(k, v)]+    -> k+    -> Property+prop_delete_member kvs k =+    (===)+        (RMap.member k (RMap.delete k (RMap.fromList kvs)))+        (OMap.member k (OMap.delete k (OMap.fromList kvs)))+    & cover 10+        (filter ((== k) . fst) kvs == [])+        "filter ((== k) . fst) kvs == []"+    & cover 10+        (filter ((== k) . fst) kvs /= [])+        "filter ((== k) . fst) kvs /= []"++prop_delete_toList+    :: forall k v. (Ord k, Show k, Eq v, Show v)+    => [(k, v)]+    -> k+    -> Property+prop_delete_toList kvs k =+    (===)+        (RMap.toList (RMap.delete k (RMap.fromList kvs)))+        (OMap.toList (OMap.delete k (OMap.fromList kvs)))+    & cover 10+        (filter ((== k) . fst) kvs == [])+        "filter ((== k) . fst) kvs == []"+    & cover 10+        (filter ((== k) . fst) kvs /= [])+        "filter ((== k) . fst) kvs /= []"++--------------------------------------------------------------------------------+-- Insert+--------------------------------------------------------------------------------++prop_insert_lookup+    :: forall k v. (Ord k, Eq v, Show v)+    => [(k, v)]+    -> k+    -> v+    -> Property+prop_insert_lookup kvs k v =+    (===)+        (RMap.lookup k (RMap.insert k v (RMap.fromList kvs)))+        (OMap.lookup k (OMap.insert k v (OMap.fromList kvs)))+    & cover 10+        (filter ((== k) . fst) kvs == [])+        "filter ((== k) . fst) kvs == []"+    & cover 10+        (filter ((== k) . fst) kvs /= [])+        "filter ((== k) . fst) kvs /= []"++prop_insert_member+    :: forall k v. (Ord k, Eq v)+    => [(k, v)]+    -> k+    -> v+    -> Property+prop_insert_member kvs k v =+    (===)+        (RMap.member k (RMap.insert k v (RMap.fromList kvs)))+        (OMap.member k (OMap.insert k v (OMap.fromList kvs)))+    & cover 10+        (filter ((== k) . fst) kvs == [])+        "filter ((== k) . fst) kvs == []"+    & cover 10+        (filter ((== k) . fst) kvs /= [])+        "filter ((== k) . fst) kvs /= []"++prop_insert_toList+    :: forall k v. (Ord k, Show k, Eq v, Show v)+    => [(k, v)]+    -> k+    -> v+    -> Property+prop_insert_toList kvs k v =+    (===)+        (RMap.toList (RMap.insert k v (RMap.fromList kvs)))+        (OMap.toList (OMap.insert k v (OMap.fromList kvs)))+    & cover 10+        (filter ((== k) . fst) kvs == [])+        "filter ((== k) . fst) kvs == []"+    & cover 10+        (filter ((== k) . fst) kvs /= [])+        "filter ((== k) . fst) kvs /= []"++--------------------------------------------------------------------------------+-- Map+--------------------------------------------------------------------------------++prop_map+    :: (Ord k, Show k, Eq v2, Show v2)+    => [(k, v1)]+    -> Fun v1 v2+    -> Property+prop_map kvs (applyFun -> f) =+    (===)+        (RMap.toList (RMap.map f (RMap.fromList kvs)))+        (OMap.toList (OMap.map f (OMap.fromList kvs)))++prop_map_mempty+    :: forall k v1 v2. (Ord k, Show k, Eq v2, Monoid v2, Show v2)+    => [(k, v1)]+    -> Property+prop_map_mempty kvs =+    (===)+        (RMap.toList (RMap.map (const (mempty @v2)) (RMap.fromList kvs)))+        (OMap.toList (OMap.map (const (mempty @v2)) (OMap.fromList kvs)))++prop_mapWithKey+    :: (Ord k, Show k, Eq v2, Show v2)+    => [(k, v1)]+    -> Fun (k, v1) v2+    -> Property+prop_mapWithKey kvs (applyFun2 -> f) =+    (===)+        (RMap.toList (RMap.mapWithKey f (RMap.fromList kvs)))+        (OMap.toList (OMap.mapWithKey f (OMap.fromList kvs)))++--------------------------------------------------------------------------------+-- MapAccum+--------------------------------------------------------------------------------++prop_mapAccumL+    :: forall s k v1 v2. (Eq s, Eq v2, Ord k, Show k, Show s, Show v2)+    => Fun (s, v1) (s, v2)+    -> s+    -> [(k, v1)]+    -> Property+prop_mapAccumL (applyFun2 -> f) s0 kvs =+    (===)+        (RMap.toList <$> rmapAccumL f s0 (RMap.fromList kvs))+        (OMap.toList <$> omapAccumL f s0 (OMap.fromList kvs))+  where+    rmapAccumL = RMap.mapAccumL+    omapAccumL = OMap.mapAccum++prop_mapAccumR+    :: forall s k v1 v2. (Eq s, Eq v2, Ord k, Show k, Show s, Show v2)+    => Fun (s, v1) (s, v2)+    -> s+    -> [(k, v1)]+    -> Property+prop_mapAccumR (applyFun2 -> f) s0 kvs =+    (===)+        (RMap.toList <$> rmapAccumR f s0 (RMap.fromList kvs))+        (OMap.toList <$> omapAccumR f s0 (OMap.fromList kvs))+  where+    rmapAccumR   = RMap.mapAccumR+    omapAccumR g = OMap.mapAccumRWithKey (\s _ v -> g s v)++--------------------------------------------------------------------------------+-- MapAccumWithKey+--------------------------------------------------------------------------------++prop_mapAccumLWithKey+    :: forall s k v1 v2. (Eq s, Eq v2, Ord k, Show k, Show s, Show v2)+    => Fun (s, k, v1) (s, v2)+    -> s+    -> [(k, v1)]+    -> Property+prop_mapAccumLWithKey (applyFun3 -> f) s0 kvs =+    (===)+        (RMap.toList <$> rmapAccumLWithKey f s0 (RMap.fromList kvs))+        (OMap.toList <$> omapAccumLWithKey f s0 (OMap.fromList kvs))+  where+    rmapAccumLWithKey = RMap.mapAccumLWithKey+    omapAccumLWithKey = OMap.mapAccumWithKey++prop_mapAccumRWithKey+    :: forall s k v1 v2. (Eq s, Eq v2, Ord k, Show k, Show s, Show v2)+    => Fun (s, k, v1) (s, v2)+    -> s+    -> [(k, v1)]+    -> Property+prop_mapAccumRWithKey (applyFun3 -> f) s0 kvs =+    (===)+        (RMap.toList <$> rmapAccumRWithKey f s0 (RMap.fromList kvs))+        (OMap.toList <$> omapAccumRWithKey f s0 (OMap.fromList kvs))+  where+    rmapAccumRWithKey = RMap.mapAccumRWithKey+    omapAccumRWithKey = OMap.mapAccumRWithKey++--------------------------------------------------------------------------------+-- Arbitrary instances+--------------------------------------------------------------------------------++instance (Arbitrary k, Ord k, Arbitrary v) =>+    Arbitrary (RMap.Map k v)+  where+    arbitrary = RMap.fromList <$> listOf ((,) <$> arbitrary <*> arbitrary)+    shrink = shrinkMapBy RMap.fromList RMap.toList shrink
+ components/monoidmap-test/Data/MonoidMap/Internal/SingletonSpec.hs view
@@ -0,0 +1,148 @@+{- HLINT ignore "Redundant bracket" -}+{- HLINT ignore "Use camelCase" -}+{- HLINT ignore "Use null" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.SingletonSpec+    ( spec+    ) where++import Prelude++import Data.Function+    ( (&) )+import Data.MonoidMap.Internal+    ( nonNullCount )+import Data.Proxy+    ( Proxy (..) )+import Test.Common+    ( Key+    , Test+    , TestValueType (TestValueType)+    , makeSpec+    , property+    , testValueTypesAll+    )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Property, cover, (===) )++import Control.Monad+    ( forM_ )+import qualified Data.MonoidMap.Internal as MonoidMap+import qualified Data.Set as Set++spec :: Spec+spec = describe "Singletons" $ do++    forM_ testValueTypesAll $+        \(TestValueType p) -> specFor (Proxy @Key) p++specFor :: forall k v. Test k v => Proxy k -> Proxy v -> Spec+specFor = makeSpec $ do++    it "prop_singleton_get" $+        prop_singleton_get+            @k @v & property+    it "prop_singleton_nonNullKey" $+        prop_singleton_nonNullKey+            @k @v & property+    it "prop_singleton_nonNullKeys" $+        prop_singleton_nonNullKeys+            @k @v & property+    it "prop_singleton_null" $+        prop_singleton_null+            @k @v & property+    it "prop_singleton_nullify" $+        prop_singleton_nullify+            @k @v & property+    it "prop_singleton_nonNullCount" $+        prop_singleton_nonNullCount+            @k @v & property+    it "prop_singleton_toList" $+        prop_singleton_toList+            @k @v & property++prop_singleton_get+    :: Test k v => k -> v -> Property+prop_singleton_get k v =+    MonoidMap.get k (MonoidMap.singleton k v) === v+    & cover 2+        (v == mempty)+        "v == mempty"+    & cover 2+        (v /= mempty)+        "v /= mempty"++prop_singleton_nonNullKey+    :: Test k v => k -> v -> Property+prop_singleton_nonNullKey k v =+    MonoidMap.nonNullKey k (MonoidMap.singleton k v) === (v /= mempty)+    & cover 2+        (v == mempty)+        "v == mempty"+    & cover 2+        (v /= mempty)+        "v /= mempty"++prop_singleton_nonNullKeys+    :: Test k v => k -> v -> Property+prop_singleton_nonNullKeys k v =+    MonoidMap.nonNullKeys (MonoidMap.singleton k v) ===+        (if v == mempty then Set.empty else Set.singleton k)+    & cover 2+        (v == mempty)+        "v == mempty"+    & cover 2+        (v /= mempty)+        "v /= mempty"++prop_singleton_null+    :: Test k v => k -> v -> Property+prop_singleton_null k v =+    MonoidMap.null (MonoidMap.singleton k v) === (v == mempty)+    & cover 2+        (v == mempty)+        "v == mempty"+    & cover 2+        (v /= mempty)+        "v /= mempty"++prop_singleton_nullify+    :: Test k v => k -> v -> Property+prop_singleton_nullify k v =+    MonoidMap.nullify k (MonoidMap.singleton k v) === mempty+    & cover 2+        (v == mempty)+        "v == mempty"+    & cover 2+        (v /= mempty)+        "v /= mempty"++prop_singleton_nonNullCount+    :: Test k v => k -> v -> Property+prop_singleton_nonNullCount k v =+    nonNullCount (MonoidMap.singleton k v) ===+        (if v == mempty then 0 else 1)+    & cover 2+        (v == mempty)+        "v == mempty"+    & cover 2+        (v /= mempty)+        "v /= mempty"++prop_singleton_toList+    :: Test k v => k -> v -> Property+prop_singleton_toList k v =+    MonoidMap.toList (MonoidMap.singleton k v) ===+        [(k, v) | v /= mempty]+    & cover 2+        (v == mempty)+        "v == mempty"+    & cover 2+        (v /= mempty)+        "v /= mempty"
+ components/monoidmap-test/Data/MonoidMap/Internal/SliceSpec.hs view
@@ -0,0 +1,139 @@+{- HLINT ignore "Redundant bracket" -}+{- HLINT ignore "Use camelCase" -}+{- HLINT ignore "Use null" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.SliceSpec+    ( spec+    , Slice (..)+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Bifunctor+    ( Bifunctor (bimap) )+import Data.Function+    ( (&) )+import Data.Monoid.Null+    ( MonoidNull )+import Data.MonoidMap.Internal+    ( MonoidMap, nonNullCount )+import Data.Proxy+    ( Proxy (..) )+import GHC.Exts+    ( IsList (..) )+import Test.Common+    ( Key+    , Test+    , TestValueType (TestValueType)+    , makeSpec+    , property+    , testValueTypesAll+    )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Arbitrary (..), Gen, Property, choose, cover, oneof, (===) )++import qualified Data.MonoidMap.Internal as MonoidMap++spec :: Spec+spec = describe "Slicing" $ do++    forM_ testValueTypesAll $+        \(TestValueType p) -> specFor (Proxy @Key) p++specFor :: forall k v. Test k v => Proxy k -> Proxy v -> Spec+specFor = makeSpec $ do++    it "prop_take_toList_fromList" $+        prop_take_toList_fromList+            @k @v & property+    it "prop_drop_toList_fromList" $+        prop_drop_toList_fromList+            @k @v & property+    it "prop_splitAt_toList_fromList" $+        prop_splitAt_toList_fromList+            @k @v & property++data Slice k v = Slice Int (MonoidMap k v)+    deriving (Eq, Show)++instance (Arbitrary k, Arbitrary v, MonoidNull v, Ord k) =>+    Arbitrary (Slice k v)+  where+    arbitrary = do+        m <- genMap+        i <- genIndex m+        pure $ Slice i m+      where+        genMap :: Gen (MonoidMap k v)+        genMap = arbitrary++        genIndex :: MonoidMap k v -> Gen Int+        genIndex m = oneof+            [ choose (negate (length m), -1)+            , pure 0+            , choose (1, length m - 1)+            , pure (length m)+            , choose (length m + 1, 2 * length m)+            ]++prop_take_toList_fromList+    :: Test k v => Slice k v -> Property+prop_take_toList_fromList (Slice i m) =+    MonoidMap.take i m+        === (fromList . Prelude.take i . toList) m+    & cover 2+        (i == 0 && 0 < nonNullCount m)+        "i == 0 && 0 < nonNullCount m"+    & cover 2+        (0 < i && i < nonNullCount m)+        "0 < i && i < nonNullCount m"+    & cover 2+        (0 < nonNullCount m && nonNullCount m == i)+        "0 < nonNullCount m && nonNullCount m == i"+    & cover 2+        (0 < nonNullCount m && nonNullCount m < i)+        "0 < nonNullCount m && nonNullCount m < i"++prop_drop_toList_fromList+    :: Test k v => Slice k v -> Property+prop_drop_toList_fromList (Slice i m) =+    MonoidMap.drop i m+        === (fromList . Prelude.drop i . toList) m+    & cover 2+        (i == 0 && 0 < nonNullCount m)+        "i == 0 && 0 < nonNullCount m"+    & cover 2+        (0 < i && i < nonNullCount m)+        "0 < i && i < nonNullCount m"+    & cover 2+        (0 < nonNullCount m && nonNullCount m == i)+        "0 < nonNullCount m && nonNullCount m == i"+    & cover 2+        (0 < nonNullCount m && nonNullCount m < i)+        "0 < nonNullCount m && nonNullCount m < i"++prop_splitAt_toList_fromList+    :: Test k v => Slice k v -> Property+prop_splitAt_toList_fromList (Slice i m) =+    MonoidMap.splitAt i m+        === (bimap fromList fromList . Prelude.splitAt i . toList) m+    & cover 2+        (i == 0 && 0 < nonNullCount m)+        "i == 0 && 0 < nonNullCount m"+    & cover 2+        (0 < i && i < nonNullCount m)+        "0 < i && i < nonNullCount m"+    & cover 2+        (0 < nonNullCount m && nonNullCount m == i)+        "0 < nonNullCount m && nonNullCount m == i"+    & cover 2+        (0 < nonNullCount m && nonNullCount m < i)+        "0 < nonNullCount m && nonNullCount m < i"
+ components/monoidmap-test/Data/MonoidMap/Internal/SuffixSpec.hs view
@@ -0,0 +1,80 @@+{- HLINT ignore "Redundant bracket" -}+{- HLINT ignore "Use camelCase" -}+{- HLINT ignore "Use null" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.SuffixSpec+    ( spec+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Function+    ( (&) )+import Data.Maybe+    ( isJust )+import Data.MonoidMap.Internal+    ( MonoidMap )+import Data.Proxy+    ( Proxy (..) )+import Data.Semigroup.Cancellative+    ( RightReductive (..) )+import Test.Common+    ( Key+    , Test+    , TestValueType (TestValueType)+    , makeSpec+    , property+    , testValueTypesRightReductive+    )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Property, cover, (===) )++import qualified Test.QuickCheck as QC++spec :: Spec+spec = describe "Suffixes" $ do++    forM_ testValueTypesRightReductive $+        \(TestValueType p) -> specFor (Proxy @Key) p++specFor+    :: forall k v. (Test k v, RightReductive v) => Proxy k -> Proxy v -> Spec+specFor = makeSpec $ do+    it "prop_stripSuffix_isJust" $+        prop_stripSuffix_isJust+            @k @v & property+    it "prop_stripSuffix_mappend" $+        prop_stripSuffix_mappend+            @k @v & property++prop_stripSuffix_isJust+    :: (Test k v, RightReductive v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+prop_stripSuffix_isJust m1 m2 =+    isJust (stripSuffix m1 m2) === m1 `isSuffixOf` m2+    & cover 1+        (m1 `isSuffixOf` m2)+        "m1 `isSuffixOf` m2"++prop_stripSuffix_mappend+    :: (Test k v, RightReductive v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+prop_stripSuffix_mappend m1 m2 = QC.property $+    all+        (\r -> r <> m1 == m2)+        (stripSuffix m1 m2)+    & cover 1+        (isJust (stripSuffix m1 m2))+        "isJust (stripSuffix m1 m2)"
+ components/monoidmap-test/Data/MonoidMap/Internal/TraversalSpec.hs view
@@ -0,0 +1,191 @@+{-# LANGUAGE StandaloneDeriving #-}+{-# OPTIONS_GHC -Wno-orphans #-}+-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.TraversalSpec+    ( spec+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Function+    ( (&) )+import Data.Functor.Identity+    ( Identity (..) )+import Data.MonoidMap.Internal+    ( MonoidMap )+import Data.Proxy+    ( Proxy (..) )+import Test.Common+    ( Key+    , Test+    , TestValueType (TestValueType)+    , makeSpec+    , property+    , testValueTypesAll+    )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Arbitrary (..)+    , Fun (..)+    , Property+    , applyFun+    , applyFun2+    , applyFun3+    , (===)+    )+import Data.Semigroup+    ( First (..), Last (..) )++import qualified Data.Map.Strict as Map+import qualified Data.MonoidMap.Internal as MonoidMap+import qualified Data.Traversable as Traversable++spec :: Spec+spec = describe "Traversal" $ do++    forM_ testValueTypesAll $+        \(TestValueType p) -> specFor (Proxy @Key) p++specFor :: forall k v. Test k v => Proxy k -> Proxy v -> Spec+specFor = makeSpec $ do++    describe "traverse" $ do++        it "prop_traverse_@Identity" $+            prop_traverse @Identity+                @k @v & property+        it "prop_traverse_@Maybe" $+            prop_traverse @Maybe+                @k @v & property+        it "prop_traverse_@First" $+            prop_traverse @First+                @k @v & property+        it "prop_traverse_@Last" $+            prop_traverse @Last+                @k @v & property++    describe "traverseWithKey" $ do++        it "prop_traverseWithKey_@Identity" $+            prop_traverseWithKey @Identity+                @k @v & property+        it "prop_traverseWithKey_@Maybe" $+            prop_traverseWithKey @Maybe+                @k @v & property+        it "prop_traverseWithKey_@First" $+            prop_traverseWithKey @First+                @k @v & property+        it "prop_traverseWithKey_@Last" $+            prop_traverseWithKey @Last+                @k @v & property++    describe "mapAccumL" $ do++        it "prop_mapAccumL_@Int" $+            prop_mapAccumL @Int+                @k @v & property+        it "prop_mapAccumL_@String" $+            prop_mapAccumL @String+                @k @v & property++    describe "mapAccumR" $ do++        it "prop_mapAccumR_@Int" $+            prop_mapAccumR @Int+                @k @v & property+        it "prop_mapAccumR_@String" $+            prop_mapAccumR @String+                @k @v & property++    describe "mapAccumLWithKey" $ do++        it "prop_mapAccumLWithKey_@Int" $+            prop_mapAccumLWithKey @Int+                @k @v & property+        it "prop_mapAccumLWithKey_@String" $+            prop_mapAccumLWithKey @String+                @k @v & property++    describe "mapAccumRWithKey" $ do++        it "prop_mapAccumRWithKey_@Int" $+            prop_mapAccumRWithKey @Int+                @k @v & property+        it "prop_mapAccumRWithKey_@String" $+            prop_mapAccumRWithKey @String+                @k @v & property++prop_traverse+    :: forall t k v. Test k v+    => (Applicative t, Eq (t (MonoidMap k v)), Show (t (MonoidMap k v)))+    => Fun v (t v)+    -> MonoidMap k v+    -> Property+prop_traverse (applyFun -> f) m =+    MonoidMap.traverse f m+    ===+    fmap MonoidMap.fromMap (Traversable.traverse f (MonoidMap.toMap m))++prop_traverseWithKey+    :: forall t k v. Test k v+    => (Applicative t, Eq (t (MonoidMap k v)), Show (t (MonoidMap k v)))+    => Fun (k, v) (t v)+    -> MonoidMap k v+    -> Property+prop_traverseWithKey (applyFun2 -> f) m =+    MonoidMap.traverseWithKey f m+    ===+    fmap MonoidMap.fromMap (Map.traverseWithKey f (MonoidMap.toMap m))++prop_mapAccumL+    :: forall s k v. (Test k v, Eq s, Show s)+    => Fun (s, v) (s, v)+    -> s+    -> MonoidMap k v+    -> Property+prop_mapAccumL (applyFun2 -> f) s m =+    MonoidMap.mapAccumL f s m+    ===+    fmap MonoidMap.fromMap (Traversable.mapAccumL f s (MonoidMap.toMap m))++prop_mapAccumR+    :: forall s k v. (Test k v, Eq s, Show s)+    => Fun (s, v) (s, v)+    -> s+    -> MonoidMap k v+    -> Property+prop_mapAccumR (applyFun2 -> f) s m =+    MonoidMap.mapAccumR f s m+    ===+    fmap MonoidMap.fromMap (Traversable.mapAccumR f s (MonoidMap.toMap m))++prop_mapAccumLWithKey+    :: forall s k v. (Test k v, Eq s, Show s)+    => Fun (s, k, v) (s, v)+    -> s+    -> MonoidMap k v+    -> Property+prop_mapAccumLWithKey (applyFun3 -> f) s m =+    MonoidMap.mapAccumLWithKey f s m+    ===+    fmap MonoidMap.fromMap (Map.mapAccumWithKey f s (MonoidMap.toMap m))++prop_mapAccumRWithKey+    :: forall s k v. (Test k v, Eq s, Show s)+    => Fun (s, k, v) (s, v)+    -> s+    -> MonoidMap k v+    -> Property+prop_mapAccumRWithKey (applyFun3 -> f) s m =+    MonoidMap.mapAccumRWithKey f s m+    ===+    fmap MonoidMap.fromMap (Map.mapAccumRWithKey f s (MonoidMap.toMap m))++deriving newtype instance Arbitrary a => Arbitrary (First a)+deriving newtype instance Arbitrary a => Arbitrary (Last a)
+ components/monoidmap-test/Data/MonoidMap/Internal/UnionSpec.hs view
@@ -0,0 +1,192 @@+{- HLINT ignore "Redundant bracket" -}+{- HLINT ignore "Use camelCase" -}+{- HLINT ignore "Use null" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.UnionSpec+    ( spec+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Function+    ( (&) )+import Data.Functor.Identity+    ( Identity (..) )+import Data.Monoid.LCM+    ( LCMMonoid )+import Data.MonoidMap.Internal+    ( MonoidMap )+import Data.Proxy+    ( Proxy (..) )+import Test.Common+    ( Key+    , Test+    , TestValueType (TestValueType)+    , makeSpec+    , property+    , testValueTypesLCMMonoid+    , testValueTypesAll+    )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( Fun (..), Property, applyFun2, conjoin, cover, expectFailure, (===) )++import qualified Data.Monoid.Null as Null+import qualified Data.MonoidMap.Internal as MonoidMap+import qualified Data.Set as Set++spec :: Spec+spec = describe "Union" $ do++    forM_ testValueTypesAll $+        \(TestValueType p) -> specMonoidNull+            (Proxy @Key) p+    forM_ testValueTypesLCMMonoid $+        \(TestValueType p) -> specLCMMonoid+            (Proxy @Key) p++specMonoidNull :: forall k v. Test k v => Proxy k -> Proxy v -> Spec+specMonoidNull = makeSpec $ do+    it "prop_unionWith_get" $+        prop_unionWith_get+            @k @v & property+    it "prop_unionWith_get_total" $+        prop_unionWith_get_total+            @k @v & property+    it "prop_unionWith_get_total_failure" $+        prop_unionWith_get_total_failure+            @k @v & property+    it "prop_unionWith_unionWithA" $+        prop_unionWith_unionWithA+            @k @v & property++specLCMMonoid+    :: forall k v. (Test k v, LCMMonoid v) => Proxy k -> Proxy v -> Spec+specLCMMonoid = makeSpec $ do+    it "prop_union_isSubmapOf" $+        prop_union_isSubmapOf+            @k @v & property++prop_union_isSubmapOf+    :: (Test k v, LCMMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+prop_union_isSubmapOf m1 m2 = conjoin+    [ m1 `MonoidMap.isSubmapOf` union_m1_m2+    , m2 `MonoidMap.isSubmapOf` union_m1_m2+    ]+    & cover 2+        (m1 /= m2 && MonoidMap.nonNull (union_m1_m2))+        "m1 /= m2 && MonoidMap.nonNull (union_m1_m2)"+  where+    union_m1_m2 = MonoidMap.union m1 m2++prop_unionWith_get+    :: Test k v+    => Fun (v, v) v+    -> MonoidMap k v+    -> MonoidMap k v+    -> k+    -> Property+prop_unionWith_get (applyFun2 -> f) m1 m2 k =+    (MonoidMap.get k result+        ===+        if keyWithinUnion+        then f (MonoidMap.get k m1) (MonoidMap.get k m2)+        else mempty)+    & cover 2+        (keyWithinUnion)+        "keyWithinUnion"+    & cover 2+        (not keyWithinUnion)+        "not keyWithinUnion"+    & cover 2+        (MonoidMap.null result)+        "MonoidMap.null result"+    & cover 2+        (MonoidMap.nonNull result)+        "MonoidMap.nonNull result)"+    & cover 2+        (MonoidMap.nullKey k result)+        "MonoidMap.nullKey k result"+    & cover 2+        (MonoidMap.nonNullKey k result)+        "MonoidMap.nonNullKey k result"+  where+    keyWithinUnion =+        k `Set.member` Set.union+            (MonoidMap.nonNullKeys m1)+            (MonoidMap.nonNullKeys m2)+    result =+        MonoidMap.unionWith f m1 m2++prop_unionWith_get_total+    :: Test k v+    => Fun (v, v) v+    -> MonoidMap k v+    -> MonoidMap k v+    -> k+    -> Property+prop_unionWith_get_total (applyFun2 -> f0) m1 m2 k =+    (MonoidMap.get k result+        ===+        f (MonoidMap.get k m1) (MonoidMap.get k m2))+    & cover 2+        (keyWithinUnion)+        "keyWithinUnion"+    & cover 2+        (not keyWithinUnion)+        "not keyWithinUnion"+    & cover 2+        (MonoidMap.null result)+        "MonoidMap.null result"+    & cover 2+        (MonoidMap.nonNull result)+        "MonoidMap.nonNull result)"+    & cover 2+        (MonoidMap.nullKey k result)+        "MonoidMap.nullKey k result"+    & cover 2+        (MonoidMap.nonNullKey k result)+        "MonoidMap.nonNullKey k result"+  where+    keyWithinUnion =+        k `Set.member` Set.union+            (MonoidMap.nonNullKeys m1)+            (MonoidMap.nonNullKeys m2)+    result =+        MonoidMap.unionWith f m1 m2+    f v1 v2+        | Null.null v1 && Null.null v2 = mempty+        | otherwise = f0 v1 v2++prop_unionWith_get_total_failure+    :: Test k v+    => Fun (v, v) v+    -> MonoidMap k v+    -> MonoidMap k v+    -> k+    -> Property+prop_unionWith_get_total_failure (applyFun2 -> f) m1 m2 k =+    expectFailure $+    MonoidMap.get k (MonoidMap.unionWith f m1 m2)+        ===+        f (MonoidMap.get k m1) (MonoidMap.get k m2)++prop_unionWith_unionWithA+    :: Test k v+    => Fun (v, v) v+    -> MonoidMap k v+    -> MonoidMap k v+    -> Property+prop_unionWith_unionWithA (applyFun2 -> f) m1 m2 =+    runIdentity (MonoidMap.unionWithA ((fmap . fmap) Identity f) m1 m2)+    ===         (MonoidMap.unionWith                          f  m1 m2)
+ components/monoidmap-test/Data/MonoidMap/Internal/ValiditySpec.hs view
@@ -0,0 +1,734 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE RankNTypes #-}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Data.MonoidMap.Internal.ValiditySpec+    ( spec+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Data+    ( Proxy (Proxy) )+import Data.Function+    ( (&) )+import Data.Functor.Identity+    ( Identity )+import Data.Group+    ( Group )+import Data.Map.Strict+    ( Map )+import Data.Maybe+    ( isJust )+import Data.Monoid.Cancellative+    ( GCDMonoid+    , LeftGCDMonoid+    , LeftReductive+    , OverlappingGCDMonoid+    , Reductive+    , RightGCDMonoid+    , RightReductive+    )+import Data.Monoid.LCM+    ( LCMMonoid )+import Data.Monoid.Monus+    ( Monus )+import Data.MonoidMap.Internal+    ( MonoidMap )+import Data.MonoidMap.Internal.SliceSpec+    ( Slice (..) )+import Data.Set+    ( Set )+import Test.Common+    ( Key+    , Test+    , TestValueType (TestValueType)+    , makeSpec+    , property+    , testValueTypesGCDMonoid+    , testValueTypesGroup+    , testValueTypesLCMMonoid+    , testValueTypesLeftGCDMonoid+    , testValueTypesLeftReductive+    , testValueTypesAll+    , testValueTypesMonus+    , testValueTypesOverlappingGCDMonoid+    , testValueTypesReductive+    , testValueTypesRightGCDMonoid+    , testValueTypesRightReductive+    )+import Test.Hspec+    ( Spec, it )+import Test.QuickCheck+    ( Fun+    , Property+    , applyFun+    , applyFun2+    , applyFun3+    , conjoin+    , counterexample+    , cover+    )++import qualified Data.Foldable as F+import qualified Data.Map.Strict as Map+import qualified Data.Monoid.Null as Null+import qualified Data.MonoidMap.Internal as MonoidMap++spec :: Spec+spec = do+    specForAll+        testValueTypesAll+        specValidMonoidNull+    specForAll+        testValueTypesLeftReductive+        specValidLeftReductive+    specForAll+        testValueTypesRightReductive+        specValidRightReductive+    specForAll+        testValueTypesReductive+        specValidReductive+    specForAll+        testValueTypesLeftGCDMonoid+        specValidLeftGCDMonoid+    specForAll+        testValueTypesRightGCDMonoid+        specValidRightGCDMonoid+    specForAll+        testValueTypesOverlappingGCDMonoid+        specValidOverlappingGCDMonoid+    specForAll+        testValueTypesGCDMonoid+        specValidGCDMonoid+    specForAll+        testValueTypesLCMMonoid+        specValidLCMMonoid+    specForAll+        testValueTypesMonus+        specValidMonus+    specForAll+        testValueTypesGroup+        specValidGroup+  where+    specForAll+        :: [TestValueType c]+        -> (forall k v. (Test k v, c v) => Proxy k -> Proxy v -> Spec)+        -> Spec+    specForAll testValueTypes specFn = forM_ testValueTypes (specFor specFn)++    specFor+        :: (forall k v. (Test k v, c v) => Proxy k -> Proxy v -> Spec)+        -> TestValueType c+        -> Spec+    specFor specFn (TestValueType (v :: Proxy v)) =+        specFn (Proxy @Key) v++specValidMonoidNull+    :: forall k v. Test k v => Proxy k -> Proxy v -> Spec+specValidMonoidNull = makeSpec $ do+    it "propValid_fromList" $+        propValid_fromList+            @k @v & property+    it "propValid_fromListWith" $+        propValid_fromListWith+            @k @v & property+    it "propValid_fromMap" $+        propValid_fromMap+            @k @v & property+    it "propValid_fromSet" $+        propValid_fromSet+            @k @v & property+    it "propValid_singleton" $+        propValid_singleton+            @k @v & property+    it "propValid_set" $+        propValid_set+            @k @v & property+    it "propValid_adjust" $+        propValid_adjust+            @k @v & property+    it "propValid_nullify" $+        propValid_nullify+            @k @v & property+    it "propValid_take" $+        propValid_take+            @k @v & property+    it "propValid_drop" $+        propValid_drop+            @k @v & property+    it "propValid_splitAt" $+        propValid_splitAt+            @k @v & property+    it "propValid_filter" $+        propValid_filter+            @k @v & property+    it "propValid_filterKeys" $+        propValid_filterKeys+            @k @v & property+    it "propValid_filterWithKey" $+        propValid_filterWithKey+            @k @v & property+    it "propValid_partition" $+        propValid_partition+            @k @v & property+    it "propValid_partitionKeys" $+        propValid_partitionKeys+            @k @v & property+    it "propValid_partitionWithKey" $+        propValid_partitionWithKey+            @k @v & property+    it "propValid_map" $+        propValid_map+            @k @v & property+    it "propValid_mapKeys" $+        propValid_mapKeys+            @k @v & property+    it "propValid_mapKeysWith" $+        propValid_mapKeysWith+            @k @v & property+    it "propValid_mapWithKey" $+        propValid_mapWithKey+            @k @v & property+    it "propValid_mapAccumL" $+        propValid_mapAccumL+            @k @v & property+    it "propValid_mapAccumR" $+        propValid_mapAccumR+            @k @v & property+    it "propValid_mapAccumLWithKey" $+        propValid_mapAccumLWithKey+            @k @v & property+    it "propValid_mapAccumRWithKey" $+        propValid_mapAccumRWithKey+            @k @v & property+    it "propValid_traverse" $+        propValid_traverse+            @k @v & property+    it "propValid_traverseWithKey" $+        propValid_traverseWithKey+            @k @v & property+    it "propValid_intersectionWith" $+        propValid_intersectionWith+            @k @v & property+    it "propValid_unionWith" $+        propValid_unionWith+            @k @v & property+    it "propValid_append" $+        propValid_append+            @k @v & property++specValidLeftReductive+    :: forall k v. (Test k v, LeftReductive v)+    => Proxy k+    -> Proxy v+    -> Spec+specValidLeftReductive = makeSpec $ do+    it "propValid_stripPrefix" $+        propValid_stripPrefix+            @k @v & property++specValidRightReductive+    :: forall k v. (Test k v, RightReductive v)+    => Proxy k+    -> Proxy v+    -> Spec+specValidRightReductive = makeSpec $ do+    it "propValid_stripSuffix" $+        propValid_stripSuffix+            @k @v & property++specValidReductive+    :: forall k v. (Test k v, Reductive v)+    => Proxy k+    -> Proxy v+    -> Spec+specValidReductive = makeSpec $ do+    it "propValid_minusMaybe" $+        propValid_minusMaybe+            @k @v & property++specValidLeftGCDMonoid+    :: forall k v. (Test k v, LeftGCDMonoid v)+    => Proxy k+    -> Proxy v+    -> Spec+specValidLeftGCDMonoid = makeSpec $ do+    it "propValid_commonPrefix" $+        propValid_commonPrefix+            @k @v & property+    it "propValid_stripCommonPrefix" $+        propValid_stripCommonPrefix+            @k @v & property++specValidRightGCDMonoid+    :: forall k v. (Test k v, RightGCDMonoid v)+    => Proxy k+    -> Proxy v+    -> Spec+specValidRightGCDMonoid = makeSpec $ do+    it "propValid_commonSuffix" $+        propValid_commonSuffix+            @k @v & property+    it "propValid_stripCommonSuffix" $+        propValid_stripCommonSuffix+            @k @v & property++specValidOverlappingGCDMonoid+    :: forall k v. (Test k v, OverlappingGCDMonoid v)+    => Proxy k+    -> Proxy v+    -> Spec+specValidOverlappingGCDMonoid = makeSpec $ do+    it "propValid_overlap" $+        propValid_overlap+            @k @v & property+    it "propValid_stripPrefixOverlap" $+        propValid_stripPrefixOverlap+            @k @v & property+    it "propValid_stripSuffixOverlap" $+        propValid_stripSuffixOverlap+            @k @v & property+    it "propValid_stripOverlap" $+        propValid_stripOverlap+            @k @v & property++specValidGCDMonoid+    :: forall k v. (Test k v, GCDMonoid v)+    => Proxy k+    -> Proxy v+    -> Spec+specValidGCDMonoid = makeSpec $ do+    it "propValid_intersection" $+        propValid_intersection+            @k @v & property++specValidLCMMonoid+    :: forall k v. (Test k v, LCMMonoid v)+    => Proxy k+    -> Proxy v+    -> Spec+specValidLCMMonoid = makeSpec $ do+    it "propValid_union" $+        propValid_union+            @k @v & property++specValidMonus+    :: forall k v. (Test k v, Monus v)+    => Proxy k+    -> Proxy v+    -> Spec+specValidMonus = makeSpec $ do+    it "propValid_monus" $+        propValid_monus+            @k @v & property++specValidGroup+    :: forall k v. (Test k v, Group v)+    => Proxy k+    -> Proxy v+    -> Spec+specValidGroup = makeSpec $ do+    it "propValid_minus" $+        propValid_minus+            @k @v & property+    it "propValid_invert" $+        propValid_invert+            @k @v & property+    it "propValid_power" $+        propValid_power+            @k @v & property++propValid+    :: Test k v => MonoidMap k v -> Property+propValid m = conjoin+    [ counterexample+        "propValid_nonNullKeys"+        (propValid_nonNullKeys)+    , counterexample+        "propValid_toList"+        (propValid_toList)+    ]+    & cover 2+        (not (Null.null m))+        "not (Null.null m)"+  where+    propValid_nonNullKeys =+        all (\k -> MonoidMap.get k m /= mempty) (MonoidMap.nonNullKeys m)+    propValid_toList =+        all (\(_, v) -> v /= mempty) (MonoidMap.toList m)++propValid_fromList+    :: Test k v => [(k, v)] -> Property+propValid_fromList kvs =+    propValid (MonoidMap.fromList kvs)+    & cover 2+        (filter (Null.null . snd) kvs /= [])+        "filter (Null.null . snd) kvs /= []"++propValid_fromListWith+    :: Test k v => Fun (v, v) v -> [(k, v)] -> Property+propValid_fromListWith (applyFun2 -> f) kvs =+    propValid (MonoidMap.fromListWith f kvs)+    & cover 2+        (filter (Null.null . snd) kvs /= [])+        "filter (Null.null . snd) kvs /= []"++propValid_fromMap+    :: Test k v => Map k v -> Property+propValid_fromMap m =+    propValid (MonoidMap.fromMap m)+    & cover 2+        (Map.filter Null.null m /= mempty)+        "Map.filter Null.null m /= mempty"++propValid_fromSet+    :: Test k v => Fun k v -> Set k -> Property+propValid_fromSet (applyFun -> f) ks =+    propValid (MonoidMap.fromSet f ks)+    & cover 2+        (Map.filter Null.null (Map.fromSet f ks) /= mempty)+        "Map.filter Null.null (Map.fromSet f ks) /= mempty"++propValid_singleton+    :: Test k v => k -> v -> Property+propValid_singleton k v =+    propValid (MonoidMap.singleton k v)+    & cover 2+        (Null.null v)+        "Null.null v"++propValid_set+    :: Test k v => k -> v -> MonoidMap k v -> Property+propValid_set k v m =+    propValid (MonoidMap.set k v m)+    & cover 2+        (Null.null v)+        "Null.null v"++propValid_adjust+    :: Test k v => Fun v v -> k -> MonoidMap k v -> Property+propValid_adjust (applyFun -> f) k m =+    propValid (MonoidMap.adjust f k m)+    & cover 1+        (Null.null (f (MonoidMap.get k m)))+        "Null.null (f (MonoidMap.get k m))"++propValid_nullify+    :: Test k v => k -> MonoidMap k v -> Property+propValid_nullify k m =+    propValid (MonoidMap.nullify k m)+    & cover 2+        (MonoidMap.nonNullKey k m)+        "MonoidMap.nonNullKey k m"++propValid_take+    :: Test k v => Slice k v -> Property+propValid_take (Slice i m) =+    propValid (MonoidMap.take i m)++propValid_drop+    :: Test k v => Slice k v -> Property+propValid_drop (Slice i m) =+    propValid (MonoidMap.drop i m)++propValid_splitAt+    :: Test k v => Slice k v -> Property+propValid_splitAt (Slice i m) =+    conjoin+        [ counterexample "propValid m1" (propValid m1)+        , counterexample "propValid m2" (propValid m2)+        ]+  where+    (m1, m2) = MonoidMap.splitAt i m++propValid_filter+    :: Test k v => Fun v Bool -> MonoidMap k v -> Property+propValid_filter (applyFun -> f) m =+    propValid (MonoidMap.filter f m)++propValid_filterKeys+    :: Test k v => Fun k Bool -> MonoidMap k v -> Property+propValid_filterKeys (applyFun -> f) m =+    propValid (MonoidMap.filterKeys f m)++propValid_filterWithKey+    :: Test k v => Fun (k, v) Bool -> MonoidMap k v -> Property+propValid_filterWithKey (applyFun2 -> f) m =+    propValid (MonoidMap.filterWithKey f m)++propValid_partition+    :: Test k v => Fun v Bool -> MonoidMap k v -> Property+propValid_partition (applyFun -> f) m =+    conjoin+        [ counterexample "propValid m1" (propValid m1)+        , counterexample "propValid m2" (propValid m2)+        ]+  where+    (m1, m2) = MonoidMap.partition f m++propValid_partitionKeys+    :: Test k v => Fun k Bool -> MonoidMap k v -> Property+propValid_partitionKeys (applyFun -> f) m =+    conjoin+        [ counterexample "propValid m1" (propValid m1)+        , counterexample "propValid m2" (propValid m2)+        ]+  where+    (m1, m2) = MonoidMap.partitionKeys f m++propValid_partitionWithKey+    :: Test k v => Fun (k, v) Bool -> MonoidMap k v -> Property+propValid_partitionWithKey (applyFun2 -> f) m =+    conjoin+        [ counterexample "propValid m1" (propValid m1)+        , counterexample "propValid m2" (propValid m2)+        ]+  where+    (m1, m2) = MonoidMap.partitionWithKey f m++propValid_map+    :: Test k v => Fun v v -> MonoidMap k v -> Property+propValid_map (applyFun -> f) m =+    propValid (MonoidMap.map f m)++propValid_mapKeys+    :: Test k v => Fun k k -> MonoidMap k v -> Property+propValid_mapKeys (applyFun -> f) m =+    propValid (MonoidMap.mapKeys f m)++propValid_mapKeysWith+    :: Test k v => Fun (v, v) v -> Fun k k -> MonoidMap k v -> Property+propValid_mapKeysWith (applyFun2 -> f) (applyFun -> g) m =+    propValid (MonoidMap.mapKeysWith f g m)++propValid_mapWithKey+    :: Test k v => Fun (k, v) v -> MonoidMap k v -> Property+propValid_mapWithKey (applyFun2 -> f) m =+    propValid (MonoidMap.mapWithKey f m)++propValid_mapAccumL+    :: forall k v s. s ~ Int+    => Test k v+    => Fun (s, v) (s, v)+    -> s+    -> MonoidMap k v+    -> Property+propValid_mapAccumL (applyFun2 -> f) s m =+    propValid $ snd $ MonoidMap.mapAccumL f s m++propValid_mapAccumR+    :: forall k v s. s ~ Int+    => Test k v+    => Fun (s, v) (s, v)+    -> s+    -> MonoidMap k v+    -> Property+propValid_mapAccumR (applyFun2 -> f) s m =+    propValid $ snd $ MonoidMap.mapAccumR f s m++propValid_mapAccumLWithKey+    :: forall k v s. s ~ Int+    => Test k v+    => Fun (s, k, v) (s, v)+    -> s+    -> MonoidMap k v+    -> Property+propValid_mapAccumLWithKey (applyFun3 -> f) s m =+    propValid $ snd $ MonoidMap.mapAccumLWithKey f s m++propValid_mapAccumRWithKey+    :: forall k v s. s ~ Int+    => Test k v+    => Fun (s, k, v) (s, v)+    -> s+    -> MonoidMap k v+    -> Property+propValid_mapAccumRWithKey (applyFun3 -> f) s m =+    propValid $ snd $ MonoidMap.mapAccumRWithKey f s m++propValid_traverse+    :: forall k v t. (Applicative t, Foldable t, Test k v)+    => t ~ Identity+    => Fun v (t v)+    -> MonoidMap k v+    -> Property+propValid_traverse (applyFun -> f) m+    = conjoin+    $ fmap propValid+    $ F.toList @t+    $ MonoidMap.traverse f m++propValid_traverseWithKey+    :: forall k v t. (Applicative t, Foldable t, Test k v)+    => t ~ Identity+    => Fun (k, v) (t v)+    -> MonoidMap k v+    -> Property+propValid_traverseWithKey (applyFun2 -> f) m+    = conjoin+    $ fmap propValid+    $ F.toList @t+    $ MonoidMap.traverseWithKey f m++propValid_intersection+    :: (Test k v, GCDMonoid v) => MonoidMap k v -> MonoidMap k v -> Property+propValid_intersection m1 m2 =+    propValid (MonoidMap.intersection m1 m2)++propValid_intersectionWith+    :: Test k v => Fun (v, v) v -> MonoidMap k v -> MonoidMap k v -> Property+propValid_intersectionWith (applyFun2 -> f) m1 m2 =+    propValid (MonoidMap.intersectionWith f m1 m2)++propValid_union+    :: (Test k v, LCMMonoid v) => MonoidMap k v -> MonoidMap k v -> Property+propValid_union m1 m2 =+    propValid (MonoidMap.union m1 m2)++propValid_unionWith+    :: Test k v => Fun (v, v) v -> MonoidMap k v -> MonoidMap k v -> Property+propValid_unionWith (applyFun2 -> f) m1 m2 =+    propValid (MonoidMap.unionWith f m1 m2)++propValid_append+    :: Test k v => MonoidMap k v -> MonoidMap k v -> Property+propValid_append m1 m2 =+    propValid (MonoidMap.append m1 m2)++propValid_minus+    :: (Test k v, Group v) => MonoidMap k v -> MonoidMap k v -> Property+propValid_minus m1 m2 =+    propValid (MonoidMap.minus m1 m2)++propValid_minusMaybe+    :: (Test k v, Reductive v) => MonoidMap k v -> MonoidMap k v -> Property+propValid_minusMaybe m1 m2 =+    maybe (property True) propValid mr+    & cover 2 (isJust mr) "isJust mr"+  where+    mr = MonoidMap.minusMaybe m1 m2++propValid_monus+    :: (Test k v, Monus v) => MonoidMap k v -> MonoidMap k v -> Property+propValid_monus m1 m2 =+    propValid (MonoidMap.monus m1 m2)++propValid_invert+    :: (Test k v, Group v) => MonoidMap k v -> Property+propValid_invert m =+    propValid (MonoidMap.invert m)++propValid_power+    :: (Test k v, Group v) => MonoidMap k v -> Int -> Property+propValid_power m i =+    propValid (MonoidMap.power m i)++propValid_commonPrefix+    :: (Test k v, LeftGCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+propValid_commonPrefix m1 m2 =+    propValid (MonoidMap.commonPrefix m1 m2)++propValid_commonSuffix+    :: (Test k v, RightGCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+propValid_commonSuffix m1 m2 =+    propValid (MonoidMap.commonSuffix m1 m2)++propValid_stripPrefix+    :: (Test k v, LeftReductive v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+propValid_stripPrefix m1 m2 =+    maybe (property True) propValid mr+    & cover 2 (isJust mr) "isJust mr"+  where+    mr = MonoidMap.stripPrefix m1 m2++propValid_stripSuffix+    :: (Test k v, RightReductive v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+propValid_stripSuffix m1 m2 =+    maybe (property True) propValid mr+    & cover 2 (isJust mr) "isJust mr"+  where+    mr = MonoidMap.stripSuffix m1 m2++propValid_stripCommonPrefix+    :: (Test k v, LeftGCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+propValid_stripCommonPrefix m1 m2 =+    conjoin+        [ counterexample "propValid r1" (propValid r1)+        , counterexample "propValid r2" (propValid r2)+        , counterexample "propValid r3" (propValid r3)+        ]+  where+    (r1, r2, r3) = MonoidMap.stripCommonPrefix m1 m2++propValid_stripCommonSuffix+    :: (Test k v, RightGCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+propValid_stripCommonSuffix m1 m2 =+    conjoin+        [ counterexample "propValid r1" (propValid r1)+        , counterexample "propValid r2" (propValid r2)+        , counterexample "propValid r3" (propValid r3)+        ]+  where+    (r1, r2, r3) = MonoidMap.stripCommonSuffix m1 m2++propValid_overlap+    :: (Test k v, OverlappingGCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+propValid_overlap m1 m2 =+    propValid (MonoidMap.overlap m1 m2)++propValid_stripPrefixOverlap+    :: (Test k v, OverlappingGCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+propValid_stripPrefixOverlap m1 m2 =+    propValid (MonoidMap.stripPrefixOverlap m1 m2)++propValid_stripSuffixOverlap+    :: (Test k v, OverlappingGCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+propValid_stripSuffixOverlap m1 m2 =+    propValid (MonoidMap.stripSuffixOverlap m1 m2)++propValid_stripOverlap+    :: (Test k v, OverlappingGCDMonoid v)+    => MonoidMap k v+    -> MonoidMap k v+    -> Property+propValid_stripOverlap m1 m2 =+    conjoin+        [ counterexample "propValid r1" (propValid r1)+        , counterexample "propValid r2" (propValid r2)+        , counterexample "propValid r3" (propValid r3)+        ]+  where+    (r1, r2, r3) = MonoidMap.stripOverlap m1 m2
+ components/monoidmap-test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ components/monoidmap-test/SpecHook.hs view
@@ -0,0 +1,6 @@+module SpecHook where++import Test.Hspec++hook :: Spec -> Spec+hook = parallel
+ components/monoidmap-test/Test/Combinators/NonZero.hs view
@@ -0,0 +1,44 @@+-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Test.Combinators.NonZero+    ( NonZero+    , genNonZero+    , getNonZero+    , maybeNonZero+    , shrinkNonZero+    )+    where++import Prelude++import Data.Group+    ( Group )+import Data.Maybe+    ( mapMaybe )+import Data.Monoid.Null+    ( MonoidNull )+import Data.Semigroup.Cancellative+    ( Commutative )+import Test.QuickCheck+    ( Gen, suchThatMap )++-- | A combinator for non-zero values.+newtype NonZero a = NonZero a+    deriving newtype (Eq, Num, Read, Show)+    deriving newtype (Semigroup, Commutative, Monoid, MonoidNull, Group)++genNonZero :: (Eq a, Num a) => Gen a -> Gen (NonZero a)+genNonZero genA = suchThatMap genA maybeNonZero++getNonZero :: NonZero a -> a+getNonZero (NonZero a) = a++maybeNonZero :: (Eq a, Num a) => a -> Maybe (NonZero a)+maybeNonZero p+    | p == 0 = Nothing+    | otherwise = Just (NonZero p)++shrinkNonZero :: (Eq a, Num a) => (a -> [a]) -> NonZero a -> [NonZero a]+shrinkNonZero shrinkA = mapMaybe maybeNonZero . shrinkA . getNonZero
+ components/monoidmap-test/Test/Common.hs view
@@ -0,0 +1,316 @@+{-# LANGUAGE ExistentialQuantification #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{- HLINT ignore "Redundant bracket" -}+{- HLINT ignore "Use camelCase" -}+{- HLINT ignore "Use null" -}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Test.Common+    ( Key+    , Test+    , TestKey+    , TestValueType (..)+    , testValueTypesAll+    , testValueTypesGroup+    , testValueTypesMonus+    , testValueTypesLeftReductive+    , testValueTypesRightReductive+    , testValueTypesReductive+    , testValueTypesLeftGCDMonoid+    , testValueTypesRightGCDMonoid+    , testValueTypesOverlappingGCDMonoid+    , testValueTypesGCDMonoid+    , testValueTypesLCMMonoid+    , TestValue+    , makeSpec+    , property+    ) where++import Prelude++import Data.Group+    ( Group )+import Data.Kind+    ( Constraint, Type )+import Data.Monoid+    ( Dual, Product, Sum )+import Data.Monoid.GCD+    ( GCDMonoid, LeftGCDMonoid, OverlappingGCDMonoid, RightGCDMonoid )+import Data.Monoid.LCM+    ( LCMMonoid )+import Data.Monoid.Monus+    ( Monus )+import Data.Monoid.Null+    ( MonoidNull )+import Data.MonoidMap.Internal+    ( MonoidMap )+import Data.Proxy+    ( Proxy (Proxy) )+import Data.Semigroup.Cancellative+    ( LeftReductive, Reductive, RightReductive )+import Data.Set+    ( Set )+import Data.Text+    ( Text )+import Data.Typeable+    ( Typeable, typeRep )+import GHC.Exts+    ( IsList (..) )+import Numeric.Natural+    ( Natural )+import Test.Hspec+    ( Spec, describe )+import Test.Key+    ( Key2, Key4 )+import Test.QuickCheck+    ( Arbitrary (..)+    , CoArbitrary (..)+    , Function (..)+    , Property+    , Testable+    , arbitrarySizedIntegral+    , checkCoverage+    , coarbitraryIntegral+    , coarbitraryShow+    , frequency+    , functionIntegral+    , functionMap+    , functionShow+    , listOf+    , scale+    , shrinkIntegral+    , shrinkMapBy+    )++import qualified Data.MonoidMap.Internal as MonoidMap+import qualified Data.Text as Text+import qualified Test.QuickCheck as QC++--------------------------------------------------------------------------------+-- Arbitrary instances+--------------------------------------------------------------------------------++instance (Arbitrary k, Ord k, Arbitrary v, MonoidNull v) =>+    Arbitrary (MonoidMap k v)+  where+    arbitrary =+        fromList <$> scale (`mod` 16) (listOf ((,) <$> arbitrary <*> arbitrary))+    shrink =+        shrinkMapBy MonoidMap.fromMap MonoidMap.toMap shrink++instance (CoArbitrary k, CoArbitrary v) =>+    CoArbitrary (MonoidMap k v)+  where+    coarbitrary = coarbitrary . MonoidMap.toMap++instance (Function k, Function v, Ord k, MonoidNull v) =>+    Function (MonoidMap k v)+  where+    function = functionMap MonoidMap.toMap MonoidMap.fromMap++instance Arbitrary Natural where+    arbitrary = arbitrarySizedIntegral+    shrink = shrinkIntegral++instance CoArbitrary Natural where+    coarbitrary = coarbitraryIntegral++instance Function Natural where+    function = functionIntegral++instance Arbitrary Text where+    arbitrary = Text.pack <$> listOf genChar+      where+        genChar = frequency+            [ (64, pure 'a')+            , (16, pure 'b')+            , ( 4, pure 'c')+            , ( 1, pure 'd')+            ]++instance CoArbitrary Text where+    coarbitrary = coarbitraryShow++instance Function Text where+    function = functionShow++--------------------------------------------------------------------------------+-- Test keys+--------------------------------------------------------------------------------++type SmallKey = Key2+type Key = Key4++--------------------------------------------------------------------------------+-- Test constraints+--------------------------------------------------------------------------------++type Test k v = (TestKey k, TestValue v)++type TestKey k =+    ( Arbitrary k+    , CoArbitrary k+    , Function k+    , Ord k+    , Show k+    , Typeable k+    )++type TestValue v =+    ( Arbitrary v+    , CoArbitrary v+    , Eq v+    , Function v+    , MonoidNull v+    , Show v+    , Typeable v+    )++--------------------------------------------------------------------------------+-- Test value types (for different type class constraints)+--------------------------------------------------------------------------------++data TestValueType (c :: Type -> Constraint) =+    forall v. (TestValue v, c v) => TestValueType (Proxy v)++testValueTypesAll :: [TestValueType MonoidNull]+testValueTypesAll =+    [ TestValueType (Proxy @(Dual Text))+    , TestValueType (Proxy @(Dual [Int]))+    , TestValueType (Proxy @(Dual [Natural]))+    , TestValueType (Proxy @(Product Int))+    , TestValueType (Proxy @(Product Natural))+    , TestValueType (Proxy @(Set Int))+    , TestValueType (Proxy @(Set Natural))+    , TestValueType (Proxy @(Sum Int))+    , TestValueType (Proxy @(Sum Natural))+    , TestValueType (Proxy @(Text))+    , TestValueType (Proxy @[Int])+    , TestValueType (Proxy @[Natural])+    , TestValueType (Proxy @(MonoidMap SmallKey (Sum Int)))+    , TestValueType (Proxy @(MonoidMap SmallKey (Sum Natural)))+    ]++testValueTypesGroup :: [TestValueType Group]+testValueTypesGroup =+    [ TestValueType (Proxy @(Sum Int))+    , TestValueType (Proxy @(MonoidMap SmallKey (Sum Int)))+    ]++testValueTypesMonus :: [TestValueType Monus]+testValueTypesMonus =+    [ TestValueType (Proxy @(Product Natural))+    , TestValueType (Proxy @(Set Int))+    , TestValueType (Proxy @(Set Natural))+    , TestValueType (Proxy @(Sum Natural))+    , TestValueType (Proxy @(MonoidMap SmallKey (Sum Natural)))+    ]++testValueTypesLeftReductive :: [TestValueType LeftReductive]+testValueTypesLeftReductive =+    [ TestValueType (Proxy @(Dual Text))+    , TestValueType (Proxy @(Dual [Int]))+    , TestValueType (Proxy @(Dual [Natural]))+    , TestValueType (Proxy @(Product Int))+    , TestValueType (Proxy @(Product Natural))+    , TestValueType (Proxy @(Set Int))+    , TestValueType (Proxy @(Set Natural))+    , TestValueType (Proxy @(Sum Int))+    , TestValueType (Proxy @(Sum Natural))+    , TestValueType (Proxy @(Text))+    , TestValueType (Proxy @[Int])+    , TestValueType (Proxy @[Natural])+    , TestValueType (Proxy @(MonoidMap SmallKey (Sum Natural)))+    ]++testValueTypesRightReductive :: [TestValueType RightReductive]+testValueTypesRightReductive =+    [ TestValueType (Proxy @(Dual Text))+    , TestValueType (Proxy @(Dual [Int]))+    , TestValueType (Proxy @(Dual [Natural]))+    , TestValueType (Proxy @(Product Int))+    , TestValueType (Proxy @(Product Natural))+    , TestValueType (Proxy @(Set Int))+    , TestValueType (Proxy @(Set Natural))+    , TestValueType (Proxy @(Sum Int))+    , TestValueType (Proxy @(Sum Natural))+    , TestValueType (Proxy @(Text))+    , TestValueType (Proxy @[Int])+    , TestValueType (Proxy @[Natural])+    , TestValueType (Proxy @(MonoidMap SmallKey (Sum Natural)))+    ]++testValueTypesReductive :: [TestValueType Reductive]+testValueTypesReductive =+    [ TestValueType (Proxy @(Product Int))+    , TestValueType (Proxy @(Product Natural))+    , TestValueType (Proxy @(Set Int))+    , TestValueType (Proxy @(Set Natural))+    , TestValueType (Proxy @(Sum Int))+    , TestValueType (Proxy @(Sum Natural))+    , TestValueType (Proxy @(MonoidMap SmallKey (Sum Natural)))+    ]++testValueTypesLeftGCDMonoid :: [TestValueType LeftGCDMonoid]+testValueTypesLeftGCDMonoid =+    [ TestValueType (Proxy @(Dual Text))+    , TestValueType (Proxy @(Product Natural))+    , TestValueType (Proxy @(Set Int))+    , TestValueType (Proxy @(Set Natural))+    , TestValueType (Proxy @(Sum Natural))+    , TestValueType (Proxy @(Text))+    , TestValueType (Proxy @(MonoidMap SmallKey (Sum Natural)))+    ]++testValueTypesRightGCDMonoid :: [TestValueType RightGCDMonoid]+testValueTypesRightGCDMonoid =+    [ TestValueType (Proxy @(Dual Text))+    , TestValueType (Proxy @(Product Natural))+    , TestValueType (Proxy @(Set Int))+    , TestValueType (Proxy @(Set Natural))+    , TestValueType (Proxy @(Sum Natural))+    , TestValueType (Proxy @(Text))+    , TestValueType (Proxy @(MonoidMap SmallKey (Sum Natural)))+    ]++testValueTypesOverlappingGCDMonoid :: [TestValueType OverlappingGCDMonoid]+testValueTypesOverlappingGCDMonoid =+    [ TestValueType (Proxy @(Dual Text))+    , TestValueType (Proxy @(Product Natural))+    , TestValueType (Proxy @(Set Int))+    , TestValueType (Proxy @(Set Natural))+    , TestValueType (Proxy @(Sum Natural))+    , TestValueType (Proxy @(Text))+    , TestValueType (Proxy @(MonoidMap SmallKey (Sum Natural)))+    ]++testValueTypesGCDMonoid :: [TestValueType GCDMonoid]+testValueTypesGCDMonoid =+    [ TestValueType (Proxy @(Product Natural))+    , TestValueType (Proxy @(Set Int))+    , TestValueType (Proxy @(Set Natural))+    , TestValueType (Proxy @(Sum Natural))+    , TestValueType (Proxy @(MonoidMap SmallKey (Sum Natural)))+    ]++testValueTypesLCMMonoid :: [TestValueType LCMMonoid]+testValueTypesLCMMonoid =+    [ TestValueType (Proxy @(Product Natural))+    , TestValueType (Proxy @(Set Int))+    , TestValueType (Proxy @(Set Natural))+    , TestValueType (Proxy @(Sum Natural))+    , TestValueType (Proxy @(MonoidMap SmallKey (Sum Natural)))+    ]++--------------------------------------------------------------------------------+-- Utilities+--------------------------------------------------------------------------------++makeSpec :: forall k v. Test k v => Spec -> Proxy k -> Proxy v -> Spec+makeSpec spec _k _v = describe (show $ typeRep (Proxy @(MonoidMap k v))) spec++property :: Testable t => t -> Property+property = checkCoverage . QC.property
+ components/monoidmap-test/Test/Hspec/Unit.hs view
@@ -0,0 +1,128 @@+{-# LANGUAGE FunctionalDependencies #-}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+module Test.Hspec.Unit where++import Prelude++import Data.Functor+    ( (<&>) )+import Test.Hspec+    ( Spec, describe, it )+import Test.QuickCheck+    ( counterexample, property )+import Text.Show.Pretty+    ( ppShow )++import qualified Data.Foldable as F++class IsUnitTestDatum d f r | d -> f, d -> r where+    params :: d -> [String]+    resultActual :: f -> d -> r+    resultExpected :: d -> r++data UnitTestDatum1 p1 r = UnitTestDatum1 p1 r+data UnitTestDatum2 p1 p2 r = UnitTestDatum2 p1 p2 r+data UnitTestDatum3 p1 p2 p3 r = UnitTestDatum3 p1 p2 p3 r+data UnitTestDatum4 p1 p2 p3 p4 r = UnitTestDatum4 p1 p2 p3 p4 r++type UnitTestData1 p1 r = [UnitTestDatum1 p1 r]+type UnitTestData2 p1 p2 r = [UnitTestDatum2 p1 p2 r]+type UnitTestData3 p1 p2 p3 r = [UnitTestDatum3 p1 p2 p3 r]+type UnitTestData4 p1 p2 p3 p4 r = [UnitTestDatum4 p1 p2 p3 p4 r]++unitTestDatum1 :: (p1, r) -> UnitTestDatum1 p1 r+unitTestDatum1 (p1, r) = UnitTestDatum1 p1 r+unitTestDatum2 :: (p1, p2, r) -> UnitTestDatum2 p1 p2 r+unitTestDatum2 (p1, p2, r) = UnitTestDatum2 p1 p2 r+unitTestDatum3 :: (p1, p2, p3, r) -> UnitTestDatum3 p1 p2 p3 r+unitTestDatum3 (p1, p2, p3, r) = UnitTestDatum3 p1 p2 p3 r+unitTestDatum4 :: (p1, p2, p3, p4, r) -> UnitTestDatum4 p1 p2 p3 p4 r+unitTestDatum4 (p1, p2, p3, p4, r) = UnitTestDatum4 p1 p2 p3 p4 r++unitTestData1 :: [(p1, r)] -> UnitTestData1 p1 r+unitTestData1 = fmap unitTestDatum1+unitTestData2 :: [(p1, p2, r)] -> UnitTestData2 p1 p2 r+unitTestData2 = fmap unitTestDatum2+unitTestData3 :: [(p1, p2, p3, r)] -> UnitTestData3 p1 p2 p3 r+unitTestData3 = fmap unitTestDatum3+unitTestData4 :: [(p1, p2, p3, p4, r)] -> UnitTestData4 p1 p2 p3 p4 r+unitTestData4 = fmap unitTestDatum4++instance Show p1 =>+    IsUnitTestDatum (UnitTestDatum1 p1 r) (p1 -> r) r+  where+    params (UnitTestDatum1 p1 _) = [show p1]+    resultActual f (UnitTestDatum1 p1 _) = f p1+    resultExpected (UnitTestDatum1 _ r) = r++instance (Show p1, Show p2) =>+    IsUnitTestDatum (UnitTestDatum2 p1 p2 r) (p1 -> p2 -> r) r+  where+    params (UnitTestDatum2 p1 p2 _) = [show p1, show p2]+    resultActual f (UnitTestDatum2 p1 p2 _) = f p1 p2+    resultExpected (UnitTestDatum2 _ _ r) = r++instance (Show p1, Show p2, Show p3) =>+    IsUnitTestDatum (UnitTestDatum3 p1 p2 p3 r) (p1 -> p2 -> p3 -> r) r+  where+    params (UnitTestDatum3 p1 p2 p3 _) = [show p1, show p2, show p3]+    resultActual f (UnitTestDatum3 p1 p2 p3 _) = f p1 p2 p3+    resultExpected (UnitTestDatum3 _ _ _ r) = r++instance (Show p1, Show p2, Show p3, Show p4) =>+    IsUnitTestDatum (UnitTestDatum4 p1 p2 p3 p4 r) (p1 -> p2 -> p3 -> p4 -> r) r+  where+    params (UnitTestDatum4 p1 p2 p3 p4 _) = [show p1, show p2, show p3, show p4]+    resultActual f (UnitTestDatum4 p1 p2 p3 p4 _) = f p1 p2 p3 p4+    resultExpected (UnitTestDatum4 _ _ _ _ r) = r++unitTestSpec+    :: forall d f r. (IsUnitTestDatum d f r, Eq r, Show r)+    => String+    -> String+    -> f+    -> [d]+    -> Spec+unitTestSpec specDescription functionName function =+    describe specDescription . mapM_ unitTest+  where+    unitTest :: d -> Spec+    unitTest d = it description+        $ property+        $ counterexample counterexampleText+        $ resultExpected d == resultActual function d+      where+        counterexampleText = unlines+            [ ""+            , "expected"+            , "/="+            , "actual"+            , ""+            , showWrap (resultExpected d)+            , "/="+            , showWrap (resultActual function d)+            ]+        description = unwords+            [ functionName+            , unwords (params d <&> \s -> "(" <> s <> ")")+            ]++--------------------------------------------------------------------------------+-- Utilities+--------------------------------------------------------------------------------++showWrap :: Show a => a -> String+showWrap x+    | singleLineMaxLengthExceeded =+        multiLine+    | otherwise =+        singleLine+  where+    multiLine = ppShow x+    singleLine = show x+    singleLineMaxLength = 80+    singleLineMaxLengthExceeded = F.length singleLine > singleLineMaxLength
+ components/monoidmap-test/Test/Key.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+-- Quasi-unique keys.+--+module Test.Key+    ( Key1+    , Key2+    , Key4+    , Key8+    )+where++import Prelude++import GHC.Generics+    ( Generic+    )+import GHC.TypeLits+    ( Nat+    )+import Test.QuickCheck+    ( Arbitrary+    , CoArbitrary+    , Function+    )+import Test.QuickCheck.Quid+    ( Latin (Latin)+    , Quid+    , Size (Size)+    )++newtype Key (size :: Nat) = Key Quid+    deriving stock (Eq, Generic, Ord)+    deriving (Read, Show) via Latin Quid+    deriving (Arbitrary) via Size size Quid+    deriving (CoArbitrary) via Quid+    deriving anyclass (Function)++type Key1 = Key 1+type Key2 = Key 2+type Key4 = Key 4+type Key8 = Key 8
+ components/monoidmap-test/Test/QuickCheck/Classes/Hspec.hs view
@@ -0,0 +1,63 @@+{-# LANGUAGE PolyKinds #-}++-- |+-- Copyright: © 2022–2025 Jonathan Knowles+-- License: Apache-2.0+--+-- Provides testing functions to check that type class instances obey laws.+--+module Test.QuickCheck.Classes.Hspec+    ( testLaws+    , testLawsMany+    ) where++import Prelude++import Control.Monad+    ( forM_ )+import Data.Proxy+    ( Proxy (..) )+import Data.Typeable+    ( Typeable, typeRep )+import Test.Hspec+    ( Spec, describe, it, parallel )+import Test.QuickCheck.Classes+    ( Laws (..) )++-- | Constructs a test to check that the given type class instance obeys the+--   given set of laws.+--+-- Example usage:+--+-- >>> testLaws @Natural ordLaws+-- >>> testLaws @(Map Int) functorLaws+--+testLaws+    :: forall a. Typeable a+    => (Proxy a -> Laws)+    -> Spec+testLaws getLaws =+    parallel $ describe description $+        forM_ (lawsProperties laws) $ uncurry it+  where+    description = mconcat+        [ "Testing "+        , lawsTypeclass laws+        , " laws for type "+        , show (typeRep $ Proxy @a)+        ]+    laws = getLaws $ Proxy @a++-- | Calls `testLaws` with multiple sets of laws.+--+-- Example usage:+--+-- >>> testLawsMany @Natural [eqLaws, ordLaws]+-- >>> testLawsMany @(Map Int) [foldableLaws, functorLaws]+--+testLawsMany+    :: forall a. Typeable a+    => [Proxy a -> Laws]+    -> Spec+testLawsMany getLawsMany =+    testLaws @a `mapM_` getLawsMany
+ monoidmap-internal.cabal view
@@ -0,0 +1,167 @@+cabal-version:  3.0+name:           monoidmap-internal+version:        0.0.0.0+bug-reports:    https://github.com/jonathanknowles/monoidmap-internal/issues+license:        Apache-2.0+license-file:   LICENSE+author:         Jonathan Knowles+maintainer:     mail@jonathanknowles.net+copyright:      2022–2025 Jonathan Knowles+category:       Data Structures+synopsis:       Internal support for monoidmap.+description:    Internal support for the monoidmap package.++extra-doc-files:+    CHANGELOG.md+    README.md++common dependency-base+    build-depends:base                          >= 4.14.3.0   && < 4.22+common dependency-containers+    build-depends:containers                    >= 0.6.5.1    && < 0.8+common dependency-deepseq+    build-depends:deepseq                       >= 1.4.4.0    && < 1.6+common dependency-groups+    build-depends:groups                        >= 0.5.3      && < 0.6+common dependency-hspec+    build-depends:hspec                         >= 2.10.9     && < 2.12+common dependency-monoid-subclasses+    build-depends:monoid-subclasses             >= 1.2.3      && < 1.3+common dependency-nothunks+    build-depends:nothunks                      >= 0.1.3      && < 0.4+common dependency-pretty-show+    build-depends:pretty-show                   >= 1.10       && < 1.11+common dependency-QuickCheck+    build-depends:QuickCheck                    >= 2.14.2     && < 2.16+common dependency-quickcheck-classes+    build-depends:quickcheck-classes            >= 0.6.5.0    && < 0.7+common dependency-quickcheck-groups+    build-depends:quickcheck-groups             >= 0.0.0.0    && < 0.1+common dependency-quickcheck-monoid-subclasses+    build-depends:quickcheck-monoid-subclasses  >= 0.3.0.0    && < 0.4+common dependency-quickcheck-quid+    build-depends:quickcheck-quid               >= 0.0.1.7    && < 0.1+common dependency-tasty-bench+    build-depends:tasty-bench                   >= 0.3.2      && < 0.5+common dependency-tasty-hunit+    build-depends:tasty-hunit                   >= 0.10.0.3   && < 0.11+common dependency-text+    build-depends:text                          >= 1.2.4.1    && < 2.2++common extensions+    default-extensions:+        BangPatterns+        ConstraintKinds+        DerivingStrategies+        DerivingVia+        FlexibleContexts+        FlexibleInstances+        GeneralizedNewtypeDeriving+        LambdaCase+        MultiParamTypeClasses+        NoImplicitPrelude+        NumericUnderscores+        ScopedTypeVariables+        TupleSections+        TypeApplications+        TypeFamilies+        TypeOperators+        ViewPatterns++source-repository head+    type: git+    location: https://github.com/jonathanknowles/monoidmap++library+    import:+      , dependency-base+      , dependency-containers+      , dependency-deepseq+      , dependency-groups+      , dependency-monoid-subclasses+      , dependency-nothunks+      , extensions+    hs-source-dirs:+        components/monoidmap-internal+    exposed-modules:+        Data.MonoidMap.Internal+        Data.MonoidMap.Internal.RecoveredMap+        Data.MonoidMap.Internal.Unsafe+    default-language:+        Haskell2010++benchmark monoidmap-benchmark+    import:+      , dependency-base+      , dependency-containers+      , dependency-deepseq+      , dependency-tasty-bench+      , dependency-tasty-hunit+      , extensions+    build-depends:+      , monoidmap-internal+    default-language:+        Haskell2010+    type:+        exitcode-stdio-1.0+    hs-source-dirs:+        components/monoidmap-benchmark+    main-is:+        Main.hs++test-suite monoidmap-test+    import:+      , dependency-base+      , dependency-containers+      , dependency-groups+      , dependency-hspec+      , dependency-monoid-subclasses+      , dependency-pretty-show+      , dependency-QuickCheck+      , dependency-quickcheck-classes+      , dependency-quickcheck-groups+      , dependency-quickcheck-monoid-subclasses+      , dependency-quickcheck-quid+      , dependency-text+      , extensions+    build-depends:+      , monoidmap-internal+    ghc-options:+        -threaded -with-rtsopts=-N+    main-is:+        Spec.hs+    hs-source-dirs:+        components/monoidmap-test+    other-modules:+        SpecHook+        Data.MonoidMap.Internal.AccessSpec+        Data.MonoidMap.Internal.ClassSpec+        Data.MonoidMap.Internal.ComparisonSpec+        Data.MonoidMap.Internal.ConversionSpec+        Data.MonoidMap.Internal.DistributivitySpec+        Data.MonoidMap.Internal.ExampleSpec+        Data.MonoidMap.Internal.FilterSpec+        Data.MonoidMap.Internal.FoldSpec+        Data.MonoidMap.Internal.IntersectionSpec+        Data.MonoidMap.Internal.MapSpec+        Data.MonoidMap.Internal.MembershipSpec+        Data.MonoidMap.Internal.PartitionSpec+        Data.MonoidMap.Internal.PrefixSpec+        Data.MonoidMap.Internal.RecoveredMapSpec+        Data.MonoidMap.Internal.SingletonSpec+        Data.MonoidMap.Internal.SliceSpec+        Data.MonoidMap.Internal.SuffixSpec+        Data.MonoidMap.Internal.TraversalSpec+        Data.MonoidMap.Internal.UnionSpec+        Data.MonoidMap.Internal.ValiditySpec+        Test.Combinators.NonZero+        Test.Common+        Test.Hspec.Unit+        Test.Key+        Test.QuickCheck.Classes.Hspec+    type:+        exitcode-stdio-1.0+    default-language:+        Haskell2010+    build-tool-depends:+        hspec-discover:hspec-discover ==2.*