packages feed

nonempty-vector 0.2.4 → 0.3

raw patch · 19 files changed

+3226/−90 lines, 19 filesdep ~deepseqPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: deepseq

API changes (from Hackage documentation)

+ Data.Vector.NonEmpty: group :: Eq a => NonEmptyVector a -> NonEmpty (NonEmptyVector a)
+ Data.Vector.NonEmpty: groupBy :: Eq a => (a -> a -> Bool) -> NonEmptyVector a -> NonEmpty (NonEmptyVector a)
+ Data.Vector.NonEmpty: intersperse :: a -> NonEmptyVector a -> NonEmptyVector a
- Data.Vector.NonEmpty: create :: (forall s. ST s (MVector s a)) -> Maybe (NonEmptyVector a)
+ Data.Vector.NonEmpty: create :: (forall s. () => ST s (MVector s a)) -> Maybe (NonEmptyVector a)
- Data.Vector.NonEmpty: createT :: Traversable t => (forall s. ST s (t (MVector s a))) -> t (Maybe (NonEmptyVector a))
+ Data.Vector.NonEmpty: createT :: Traversable t => (forall s. () => ST s (t (MVector s a))) -> t (Maybe (NonEmptyVector a))
- Data.Vector.NonEmpty: modify :: (forall s. MVector s a -> ST s ()) -> NonEmptyVector a -> NonEmptyVector a
+ Data.Vector.NonEmpty: modify :: (forall s. () => MVector s a -> ST s ()) -> NonEmptyVector a -> NonEmptyVector a
- Data.Vector.NonEmpty: unsafeCreate :: (forall s. ST s (MVector s a)) -> NonEmptyVector a
+ Data.Vector.NonEmpty: unsafeCreate :: (forall s. () => ST s (MVector s a)) -> NonEmptyVector a
- Data.Vector.NonEmpty: unsafeCreateT :: Traversable t => (forall s. ST s (t (MVector s a))) -> t (NonEmptyVector a)
+ Data.Vector.NonEmpty: unsafeCreateT :: Traversable t => (forall s. () => ST s (t (MVector s a))) -> t (NonEmptyVector a)

Files

CHANGELOG.md view
@@ -1,5 +1,14 @@ # Revision history for nonempty-vector +## 0.3++* Add `group` and `groupBy`, returning non-empty lists of non-empty vector slices.+* Add `intersperse`.+* Expand property tests across the immutable and mutable APIs, including grouping,+  construction, conversions, folds, scans, and type class instances.+* Support GHC 9.14 and `base < 4.23`; update the CI matrix for GHC 9.4–9.14.+* Fix Haddock references and restore the BSD-3-Clause license text.+ ## 0.2.3  * Support GHC 9.8.x
LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2019-2026 Emily Pillmore++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Emily Pillmore nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
nonempty-vector.cabal view
@@ -1,6 +1,6 @@ cabal-version:   3.0 name:            nonempty-vector-version:         0.2.4+version:         0.3 synopsis:        Non-empty vectors description:     Performant, non-empty mutable and immutable vectors homepage:        https://github.com/emilypi/nonempty-vector@@ -9,7 +9,7 @@ license-file:    LICENSE author:          Emily Pillmore maintainer:      emilypi@cohomolo.gy-copyright:       (c) 2019-2024 Emily Pillmore <emilypi@cohomolo.gy>+copyright:       (c) 2019-2026 Emily Pillmore <emilypi@cohomolo.gy> category:        Data build-type:      Simple extra-doc-files:@@ -17,11 +17,12 @@   README.md  tested-with:-  GHC ==9.4.4-   || ==9.6.6+  GHC ==9.4.8+   || ==9.6.7    || ==9.8.4-   || ==9.10.1-   || ==9.12.1+   || ==9.10.3+   || ==9.12.4+   || ==9.14.1  source-repository head   type:     git@@ -34,8 +35,8 @@     Data.Vector.NonEmpty.Mutable    build-depends:-      base       >=4.16  && <4.22-    , deepseq+      base       >=4.16 && <4.23+    , deepseq    >=1.4  && <1.6     , primitive  >=0.6  && <0.10     , vector     >=0.12 && <0.14 @@ -47,8 +48,23 @@   default-language:  Haskell2010   type:              exitcode-stdio-1.0   main-is:           Main.hs+  other-modules:+    Test.Data.Vector.NonEmpty.Accessors+    Test.Data.Vector.NonEmpty.Construction+    Test.Data.Vector.NonEmpty.Conversion+    Test.Data.Vector.NonEmpty.Elementwise+    Test.Data.Vector.NonEmpty.Folds+    Test.Data.Vector.NonEmpty.Grouping+    Test.Data.Vector.NonEmpty.Instances+    Test.Data.Vector.NonEmpty.Modification+    Test.Data.Vector.NonEmpty.Mutable+    Test.Data.Vector.NonEmpty.Predicates+    Test.Data.Vector.NonEmpty.Scans+    Test.Data.Vector.NonEmpty.Util+   build-depends:-      base     >=4.16 && <4.22+      base     >=4.16 && <4.23+    , deepseq     , nonempty-vector     , QuickCheck     , tasty
src/Data/Vector/NonEmpty.hs view
@@ -1,13 +1,13 @@ {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE CPP #-} -- | -- Module       : Data.Vector.NonEmpty--- Copyright 	: (c) 2019-2024 Emily Pillmore+-- Copyright 	: (c) 2019-2026 Emily Pillmore -- License	: BSD-style------ Maintainer	: Emily Pillmore <emilypi@cohomolo.gy>--- Stability	: Experimental--- Portability	: DataTypeable, CPP+-- Maintainer   : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability    : Experimental+-- Portability  : DataTypeable, CPP -- -- A library for non-empty boxed vectors (that is, polymorphic arrays capable of -- holding any Haskell value). Non-empty vectors come in two flavors:@@ -18,11 +18,11 @@ -- -- This library attempts to provide support for all standard 'Vector' operations -- in the API, with some slight variation in types and implementation. For example,--- since 'head' and 'foldr' are always gauranteed to be over a non-empty 'Vector',+-- since 'head' and 'foldr' are always guaranteed to be over a non-empty 'Vector', -- it is safe to make use of the 'unsafe-*' 'Vector' operations and semigroupal -- folds available in the API in lieu of the standard implementations. ----- In contrast, some operations such as 'filter' may "break out" of a 'NonEmptyVector'+-- In contrast, some operations such as 'filter' may "break out" of a t'NonEmptyVector' -- due to the fact that there are no guarantees that may be made on the types of -- 'Bool'-valued functions passed in, hence one could write the following: --@@ -31,10 +31,10 @@ -- @ -- -- which always produces an empty vector. Thus, some operations must return either--- a 'Maybe' containing a 'NonEmptyVector' or a 'Vector' whenever appropriate. Generally+-- a 'Maybe' containing a t'NonEmptyVector' or a 'Vector' whenever appropriate. Generally -- The former is used in initialization and generation operations, and the latter -- is used in iterative operations where the intent is not to create an instance--- of 'NonEmptyVector'.+-- of t'NonEmptyVector'. -- -- Credit to Roman Leshchinskiy for the original Vector library  upon which this is based. --@@ -180,20 +180,35 @@ , scanl, scanl', scanl1, scanl1', iscanl, iscanl' , prescanr, prescanr', postscanr, postscanr' , scanr, scanr', scanr1, scanr1', iscanr, iscanr'++  -- ** Transformations+, intersperse++  -- ** Grouping+, group+, groupBy+ ) where   import Prelude ( Bool, Eq, Ord, Num, Enum-               , (.), Ordering, max, uncurry, snd)+               , (.), Ordering, max, uncurry, snd+               , pure, (+), otherwise, (>=), (-), (*), ($), (<=)+               , (==), not)  import Control.Monad (Monad) import Control.Monad.ST  import qualified Data.Foldable as Foldable import Data.Either (Either(..))+#if MIN_VERSION_base(4,19,0)+-- This import will only execute if compiling with base >= 4.19.0.0 (GHC 9.8+) import Data.Functor hiding (unzip)+#else+import Data.Functor+#endif import Data.Int-import Data.List.NonEmpty (NonEmpty(..))+import Data.List.NonEmpty (NonEmpty(..), (<|)) import qualified Data.List.NonEmpty as NonEmpty import Data.Maybe (Maybe(..)) import Data.Semigroup (Semigroup(..), (<>))@@ -201,7 +216,7 @@ import Data.Vector (Vector) import qualified Data.Vector as V import qualified Data.Vector.Generic as G-import Data.Vector.Mutable (MVector)+import Data.Vector.Mutable (MVector, unsafeWrite) import Data.Vector.NonEmpty.Internal  @@ -228,8 +243,8 @@ length = V.length . _neVec {-# INLINE length #-} --- | /O(1)/ First element. Since head is gauranteed, bounds checks--- are bypassed by deferring to 'unsafeHead'.+-- | /O(1)/ First element. Since head is guaranteed, bounds checks+-- are bypassed by deferring to 'Data.Vector.unsafeHead'. -- -- -- >>> head $ unsafeFromList [1..10]@@ -239,8 +254,8 @@ head = V.unsafeHead . _neVec {-# INLINE head #-} --- | /O(1)/ Last element. Since a last element is gauranteed, bounds checks--- are bypassed by deferring to 'unsafeLast'.+-- | /O(1)/ Last element. Since a last element is guaranteed, bounds checks+-- are bypassed by deferring to 'Data.Vector.unsafeLast'. -- -- -- >>> last $ unsafeFromList [1..10]@@ -287,7 +302,7 @@ -- The monad allows operations to be strict in the non-empty vector when -- necessary. ----- See 'V.indexM' for more details+-- See 'Data.Vector.indexM' for more details -- -- -- >>> indexM @[] (unsafeFromList [1..10]) 3@@ -299,10 +314,10 @@  -- | /O(1)/ First element of a non-empty vector in a monad. ----- See 'V.indexM' for an explanation of why this is useful.+-- See 'Data.Vector.indexM' for an explanation of why this is useful. ----- Note that this function defers to 'unsafeHeadM' since head is--- gauranteed to be safe by construction.+-- Note that this function defers to 'Data.Vector.unsafeHeadM' since head is+-- guaranteed to be safe by construction. -- -- -- >>> headM @[] (unsafeFromList [1..10])@@ -312,11 +327,11 @@ headM (NonEmptyVector v) = V.unsafeHeadM v {-# INLINE headM #-} --- | /O(1)/ Last element of a non-empty vector in a monad. See 'V.indexM' for an--- explanation of why this is useful.+-- | /O(1)/ Last element of a non-empty vector in a monad. See 'Data.Vector.indexM'+-- for an explanation of why this is useful. ----- Note that this function defers to 'unsafeHeadM' since a last element is--- gauranteed.+-- Note that this function defers to 'Data.Vector.unsafeLastM' since a last element is+-- guaranteed. -- -- -- >>> lastM @[] (unsafeFromList [1..10])@@ -1731,7 +1746,7 @@ {-# INLINE izipWith6 #-}  -- | /O(min(n,m))/ Elementwise pairing of non-empty vector elements. This is a special case--- of 'zipWith' where the function argument is '(,)'+-- of 'zipWith' where the function argument is @(,)@ -- zip :: NonEmptyVector a -> NonEmptyVector b -> NonEmptyVector (a, b) zip a b = NonEmptyVector (V.zip a' b')@@ -2009,7 +2024,7 @@ -- -- If no elements satisfy the predicate, the resulting vector may be empty. ----- TODO: this should be a more efficient function in `vector`.+-- TODO: this should be a more efficient function in @vector@. -- -- >>> ifilterM (\i a -> if a == 2 || i == 0 then Just False else Just True) (unsafeFromList [1..3]) -- Just [3]@@ -2646,3 +2661,90 @@ iscanr' :: (Int -> a -> b -> b) -> b -> NonEmptyVector a -> NonEmptyVector b iscanr' f b = NonEmptyVector . V.iscanr' f b . _neVec {-# INLINE iscanr' #-}++-- | /O(n)/ The 'intersperse' function takes an element and a 'NonEmptyVector'+-- and "intersperses" that element between the elements of the 'NonEmptyVector'.+--+-- >>> intersperse 0 (unsafeFromList [1,2,3])+-- [1,0,2,0,3]+--+-- >>> intersperse 0 (singleton 1)+-- [1]+--+intersperse :: a -> NonEmptyVector a -> NonEmptyVector a+intersperse sep nev@(NonEmptyVector v)+    | V.length v <= 1 = nev+    | otherwise = unsafeCreate $ do+        let n = V.length v+        let newLen = 2*n - 1+        mv <- V.unsafeThaw $ V.replicate newLen (V.unsafeHead v)  -- Create mutable vector++        -- Fill the first element+        unsafeWrite mv 0 (V.unsafeIndex v 0)++        -- Fill remaining elements with separators+        let go i j+              | j >= n    = pure ()+              | otherwise = do+                  unsafeWrite mv i sep+                  unsafeWrite mv (i+1) (V.unsafeIndex v j)+                  go (i+2) (j+1)++        go 1 1+        pure mv+{-# INLINE intersperse #-}++-- | /O(n)/ Split a vector into a list of slices of the input vector.+--+-- The concatenation of this list of slices is equal to the argument vector,+-- and each slice contains only equal elements. Returns nothing if no equal+-- elements are found.+--+-- This is the equivalent of 'groupBy (==)'.+--+-- >>> import qualified Data.NonEmptyVector as NEV+-- >>> NEV.group (NEV.unsafeFromList "Mississippi")+-- "M" :| ["i","ss","i","ss","i","pp","i"]+--+-- See also 'Data.Vector.group'.+--+-- @since 0.3+--+group :: Eq a => NonEmptyVector a -> NonEmpty (NonEmptyVector a)+group = groupBy (==)++-- | /O(n)/ Split a vector into a list of slices.+--+--+-- The concatenation of this list of slices is equal to the argument vector,+-- and each slice contains only equal elements, as determined by the equality+-- predicate function.+--++-- >>> import qualified Data.NonEmptyVector as NEV+-- >>> import Data.Char (isUpper)+-- >>> NEV.groupBy (\a b -> isUpper a == isUpper b) (NEV.unsafeFromList "Mississippi River")+-- "M" :| ["ississippi ","R","iver"]+--+-- See also 'Data.Vector.groupBy'.+--+-- @since 0.3+--+groupBy+    :: Eq a+    => (a -> a -> Bool)+    -> NonEmptyVector a+    -> NonEmpty (NonEmptyVector a)+groupBy f = go+  where+    go v =+      let+        h = head v+        t = NonEmptyVector $ tail v+      in case findIndex (not . f h) t of+           Nothing -> v :| []+           Just n ->+             let+               z = NonEmptyVector $ unsafeTake (n+1) v+               x = NonEmptyVector $ unsafeDrop (n+1) v+             in z <| go x
src/Data/Vector/NonEmpty/Internal.hs view
@@ -4,7 +4,7 @@ {-# LANGUAGE LambdaCase #-} -- | -- Module      : Data.Vector.NonEmpty.Internal--- Copyright   : (c) 2019-2024 Emily Pillmore+-- Copyright   : (c) 2019-2026 Emily Pillmore -- License     : BSD-style -- -- Maintainer  : Emily Pillmore <emilypi@cohomolo.gy>@@ -12,7 +12,7 @@ -- Portability : non-portable -- -- Internal module exposing the constructors for--- 'NonEmptyVector' and 'NonEmptyMVector'.+-- t'NonEmptyVector' and t'NonEmptyMVector'. -- -- /Warning/: Since the constructors are exposed here, by using this -- module, you take on the risks that you break the non-emptiness@@ -54,13 +54,13 @@ -- ---------------------------------------------------------------------- -- -- Non-empty immutable vectors --- | 'NonEmptyVector' is a thin wrapper around 'Vector' that+-- | t'NonEmptyVector' is a thin wrapper around 'Data.Vector.Vector' that -- witnesses an API requiring non-empty construction, -- initialization, and generation of non-empty vectors by design. -- -- A newtype wrapper was chosen so that no new pointer indirection--- is introduced when working with 'Vector's, and all performance--- characteristics inherited from the 'Vector' API still apply.+-- is introduced when working with 'Data.Vector.Vector's, and all performance+-- characteristics inherited from the "Data.Vector" API still apply. -- -- @since 0.2.1.0 --@@ -98,8 +98,8 @@ instance Foldable1 NonEmptyVector where     foldMap1 f (NonEmptyVector v) =         let-          h = V.unsafeHead v -- gauranteed head (nonemptiness)-          t = V.unsafeTail v -- gauranteed possibly empty tail+          h = V.unsafeHead v -- guaranteed head (nonemptiness)+          t = V.unsafeTail v -- guaranteed possibly empty tail         in go (f h) t -- therefore this is a sound call       where         go x xs@@ -119,7 +119,7 @@ -- ---------------------------------------------------------------------- -- -- Non-empty mutable vectors --- | 'NonEmptyMVector' is a thin wrapper around 'MVector' that+-- | t'NonEmptyMVector' is a thin wrapper around 'MVector' that -- witnesses an API requiring non-empty construction, -- initialization, and generation of non-empty vectors by design. --@@ -135,13 +135,13 @@     deriving (Typeable) #endif --- | 'NonEmptyMVector' parametrized by 'PrimState'+-- | t'NonEmptyMVector' parametrized by 'Control.Monad.Primitive.PrimState' 'IO'. -- -- @since 0.2.1.0 -- type NonEmptyIOVector = NonEmptyMVector RealWorld --- | 'NonEmptyMVector' parametrized by 'ST'+-- | t'NonEmptyMVector' parametrized by @t'Control.Monad.Primitive.PrimState' ('ST' s)@. -- -- @since 0.2.1.0 --
src/Data/Vector/NonEmpty/Mutable.hs view
@@ -3,7 +3,7 @@ {-# LANGUAGE NoImplicitPrelude #-} -- | -- Module      : Data.Vector.NonEmpty.Mutable--- Copyright   : (c) 2019-2024 Emily Pillmore+-- Copyright   : (c) 2019-2026 Emily Pillmore -- License     : BSD-style -- -- Maintainer  : Emily Pillmore <emilypi@cohomolo.gy>
test/Main.hs view
@@ -1,58 +1,42 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RankNTypes #-} -- | -- Module       : Main (tests)--- Copyright 	: 2019-2023 (c) Emily Pillmore+-- Copyright 	: (c) 2019-2026 Emily Pillmore -- License	: BSD------ Maintainer	: Emily Pillmore <emilypi@cohomolo.gy>--- Stability	: Experimental--- Portability	: TypeFamilies+-- Maintainer   : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability    : Experimental+-- Portability  : TypeFamilies -- module Main ( main ) where  -import Data.Maybe-import Data.Vector (Vector)-import Data.Vector.NonEmpty (NonEmptyVector)-import qualified Data.Vector.NonEmpty as NEV+import qualified Test.Data.Vector.NonEmpty.Accessors as Accessors+import qualified Test.Data.Vector.NonEmpty.Construction as Construction+import qualified Test.Data.Vector.NonEmpty.Conversion as Conversion+import qualified Test.Data.Vector.NonEmpty.Elementwise as Elementwise+import qualified Test.Data.Vector.NonEmpty.Folds as Folds+import qualified Test.Data.Vector.NonEmpty.Grouping as Grouping+import qualified Test.Data.Vector.NonEmpty.Instances as Instances+import qualified Test.Data.Vector.NonEmpty.Modification as Modification+import qualified Test.Data.Vector.NonEmpty.Mutable as Mutable+import qualified Test.Data.Vector.NonEmpty.Predicates as Predicates+import qualified Test.Data.Vector.NonEmpty.Scans as Scans -import Test.QuickCheck import Test.Tasty-import Test.Tasty.QuickCheck + main :: IO ()-main = defaultMain $ testGroup "NonEmptyVector constructor"-    [ testProperty "prop_reverse" prop_reverse-    , testProperty "prop_from_to_list" prop_from_to_list-    , testProperty "prop_from_to_vec" prop_from_to_vec+main = defaultMain $ testGroup "NonEmptyVector tests"+    [ Accessors.tests+    , Construction.tests+    , Conversion.tests+    , Modification.tests+    , Elementwise.tests+    , Predicates.tests+    , Grouping.tests+    , Folds.tests+    , Scans.tests+    , Instances.tests+    , Mutable.tests     ]--genList :: Gen [Int]-genList = listOf1 (choose (1, 100))--genNEV :: Gen (NonEmptyVector Int)-genNEV = fmap (fromJust . NEV.fromList) genList--genV :: Gen (Vector Int)-genV = NEV.toVector <$> genNEV--prop_reverse :: Property-prop_reverse =  forAll genNEV $ \t ->-  NEV.reverse (NEV.reverse t) == t--prop_from_to_list :: Property-prop_from_to_list =-  forAll genNEV $ \t ->-  forAll genList $ \u ->-    NEV.fromList (NEV.toList t) == Just t-      && fmap NEV.toList (NEV.fromList u) == Just u--prop_from_to_vec :: Property-prop_from_to_vec =-  forAll genNEV $ \t ->-  forAll genV $ \u ->-    NEV.fromVector (NEV.toVector t) == Just t-      && (NEV.toVector <$> NEV.fromVector u) == Just u
+ test/Test/Data/Vector/NonEmpty/Accessors.hs view
@@ -0,0 +1,181 @@+-- |+-- Module      : Test.Data.Vector.NonEmpty.Accessors+-- Copyright   : (c) 2019-2026 Emily Pillmore+-- License     : BSD-style+--+-- Maintainer  : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability   : experimental+-- Portability : non-portable+--+-- Properties for length information, indexing, and slicing.+--+module Test.Data.Vector.NonEmpty.Accessors+( tests+) where+++import Data.Functor.Identity (Identity(..))+import qualified Data.List as List+import qualified Data.Vector as V+import Data.Vector.NonEmpty (NonEmptyVector)+import qualified Data.Vector.NonEmpty as NEV++import Test.Data.Vector.NonEmpty.Util+import Test.QuickCheck+import Test.Tasty+import Test.Tasty.QuickCheck+++tests :: TestTree+tests = testGroup "Accessors"+    [ testGroup "Length information"+      [ testProperty "prop_length" prop_length+      ]+    , testGroup "Indexing"+      [ testProperty "prop_head" prop_head+      , testProperty "prop_last" prop_last+      , testProperty "prop_index" prop_index+      , testProperty "prop_index_out_of_bounds" prop_index_out_of_bounds+      , testProperty "prop_index_maybe" prop_index_maybe+      , testProperty "prop_unsafeIndex" prop_unsafeIndex+      ]+    , testGroup "Monadic indexing"+      [ testProperty "prop_headM" prop_headM+      , testProperty "prop_lastM" prop_lastM+      , testProperty "prop_indexM" prop_indexM+      , testProperty "prop_indexM_out_of_bounds" prop_indexM_out_of_bounds+      , testProperty "prop_unsafeIndexM" prop_unsafeIndexM+      ]+    , testGroup "Slicing"+      [ testProperty "prop_tail" prop_tail+      , testProperty "prop_init" prop_init+      , testProperty "prop_uncons" prop_uncons+      , testProperty "prop_unsnoc" prop_unsnoc+      , testProperty "prop_slice" prop_slice+      , testProperty "prop_slice_out_of_bounds" prop_slice_out_of_bounds+      , testProperty "prop_take" prop_take+      , testProperty "prop_drop" prop_drop+      , testProperty "prop_splitAt" prop_splitAt+      , testProperty "prop_unsafeSlice" prop_unsafeSlice+      , testProperty "prop_unsafeTake" prop_unsafeTake+      , testProperty "prop_unsafeDrop" prop_unsafeDrop+      ]+    ]++-- ---------------------------------------------------------------------- --+-- Length information++prop_length :: NonEmptyVector Int -> Property+prop_length v = NEV.length v === List.length (elems v)++-- ---------------------------------------------------------------------- --+-- Indexing++prop_head :: NonEmptyVector Int -> Property+prop_head v = [NEV.head v] === List.take 1 (elems v)++prop_last :: NonEmptyVector Int -> Property+prop_last v = [NEV.last v] === List.take 1 (List.reverse (elems v))++prop_index :: NonEmptyVector Int -> Property+prop_index v = forAll (genIndex v) $ \i ->+    Just (v NEV.! i) === lookupList i (elems v)++prop_index_out_of_bounds :: NonEmptyVector Int -> Property+prop_index_out_of_bounds v = forAll (genOutOfBounds v) $ \i ->+    throws (v NEV.! i)++prop_index_maybe :: NonEmptyVector Int -> Int -> Property+prop_index_maybe v i = v NEV.!? i === lookupList i (elems v)++prop_unsafeIndex :: NonEmptyVector Int -> Property+prop_unsafeIndex v = forAll (genIndex v) $ \i ->+    Just (NEV.unsafeIndex v i) === lookupList i (elems v)++-- ---------------------------------------------------------------------- --+-- Monadic indexing++prop_headM :: NonEmptyVector Int -> Property+prop_headM v = [runIdentity (NEV.headM v)] === List.take 1 (elems v)++prop_lastM :: NonEmptyVector Int -> Property+prop_lastM v =+    [runIdentity (NEV.lastM v)] === List.take 1 (List.reverse (elems v))++prop_indexM :: NonEmptyVector Int -> Property+prop_indexM v = forAll (genIndex v) $ \i ->+    Just (runIdentity (NEV.indexM v i)) === lookupList i (elems v)++prop_indexM_out_of_bounds :: NonEmptyVector Int -> Property+prop_indexM_out_of_bounds v = forAll (genOutOfBounds v) $ \i ->+    throws (runIdentity (NEV.indexM v i))++prop_unsafeIndexM :: NonEmptyVector Int -> Property+prop_unsafeIndexM v = forAll (genIndex v) $ \i ->+    Just (runIdentity (NEV.unsafeIndexM v i)) === lookupList i (elems v)++-- ---------------------------------------------------------------------- --+-- Slicing++prop_tail :: NonEmptyVector Int -> Property+prop_tail v = V.toList (NEV.tail v) === List.drop 1 (elems v)++prop_init :: NonEmptyVector Int -> Property+prop_init v =+    V.toList (NEV.init v) === List.take (List.length as - 1) as+  where+    as = elems v++prop_uncons :: NonEmptyVector Int -> Property+prop_uncons v = ([a], V.toList as) === List.splitAt 1 (elems v)+  where+    (a, as) = NEV.uncons v++prop_unsnoc :: NonEmptyVector Int -> Property+prop_unsnoc v =+    (V.toList as, [a]) === List.splitAt (List.length (elems v) - 1) (elems v)+  where+    (as, a) = NEV.unsnoc v++prop_slice :: NonEmptyVector Int -> Property+prop_slice v = forAll (genSlice v) $ \(i, m) ->+    V.toList (NEV.slice i m v) === List.take m (List.drop i (elems v))++prop_slice_out_of_bounds :: NonEmptyVector Int -> Property+prop_slice_out_of_bounds v = forAll genBadSlice $ \(i, m) ->+    throws (V.toList (NEV.slice i m v))+  where+    n = NEV.length v++    genBadSlice = oneof+      [ (,) <$> choose (-10, -1) <*> choose (0, n)+      , (,) <$> choose (0, n) <*> choose (-10, -1)+      , do+          i <- choose (0, n)+          m <- choose (n - i + 1, n - i + 10)+          pure (i, m)+      ]++prop_take :: Int -> NonEmptyVector Int -> Property+prop_take n v = V.toList (NEV.take n v) === List.take n (elems v)++prop_drop :: Int -> NonEmptyVector Int -> Property+prop_drop n v = V.toList (NEV.drop n v) === List.drop n (elems v)++prop_splitAt :: Int -> NonEmptyVector Int -> Property+prop_splitAt n v =+    (V.toList as, V.toList bs) === List.splitAt n (elems v)+  where+    (as, bs) = NEV.splitAt n v++prop_unsafeSlice :: NonEmptyVector Int -> Property+prop_unsafeSlice v = forAll (genSlice v) $ \(i, m) ->+    V.toList (NEV.unsafeSlice i m v) === List.take m (List.drop i (elems v))++prop_unsafeTake :: NonEmptyVector Int -> Property+prop_unsafeTake v = forAll (choose (0, NEV.length v)) $ \n ->+    V.toList (NEV.unsafeTake n v) === List.take n (elems v)++prop_unsafeDrop :: NonEmptyVector Int -> Property+prop_unsafeDrop v = forAll (choose (0, NEV.length v)) $ \n ->+    V.toList (NEV.unsafeDrop n v) === List.drop n (elems v)
+ test/Test/Data/Vector/NonEmpty/Construction.hs view
@@ -0,0 +1,316 @@+-- |+-- Module      : Test.Data.Vector.NonEmpty.Construction+-- Copyright   : (c) 2019-2026 Emily Pillmore+-- License     : BSD-style+--+-- Maintainer  : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability   : experimental+-- Portability : non-portable+--+-- Properties for initialization, unfolding, enumeration, and+-- concatenation.+--+-- The 'Maybe'-valued constructors must yield 'Nothing' exactly when the+-- result would be empty, and the @*1@ variants must clamp their size to at+-- least one.+--+module Test.Data.Vector.NonEmpty.Construction+( tests+) where+++import qualified Data.List as List+import Data.List.NonEmpty (NonEmpty(..))+import qualified Data.Vector as V+import qualified Data.Vector.NonEmpty as NEV+import Data.Vector.NonEmpty.Internal (NonEmptyVector(..))++import Test.Data.Vector.NonEmpty.Util+import Test.QuickCheck+import Test.Tasty+import Test.Tasty.QuickCheck+++tests :: TestTree+tests = testGroup "Construction"+    [ testGroup "Initialization"+      [ testProperty "prop_singleton" prop_singleton+      , testProperty "prop_replicate" prop_replicate+      , testProperty "prop_replicate1" prop_replicate1+      , testProperty "prop_generate" prop_generate+      , testProperty "prop_generate1" prop_generate1+      , testProperty "prop_iterateN" prop_iterateN+      , testProperty "prop_iterateN1" prop_iterateN1+      ]+    , testGroup "Monadic initialization"+      [ testProperty "prop_replicateM" prop_replicateM+      , testProperty "prop_replicate1M" prop_replicate1M+      , testProperty "prop_generateM" prop_generateM+      , testProperty "prop_generate1M" prop_generate1M+      , testProperty "prop_iterateNM" prop_iterateNM+      , testProperty "prop_iterateN1M" prop_iterateN1M+      , testProperty "prop_create" prop_create+      , testProperty "prop_unsafeCreate" prop_unsafeCreate+      , testProperty "prop_createT" prop_createT+      , testProperty "prop_unsafeCreateT" prop_unsafeCreateT+      ]+    , testGroup "Unfolding"+      [ testProperty "prop_unfoldr" prop_unfoldr+      , testProperty "prop_unfoldr1" prop_unfoldr1+      , testProperty "prop_unfoldrN" prop_unfoldrN+      , testProperty "prop_unfoldr1N" prop_unfoldr1N+      , testProperty "prop_unfoldrM" prop_unfoldrM+      , testProperty "prop_unfoldr1M" prop_unfoldr1M+      , testProperty "prop_unfoldrNM" prop_unfoldrNM+      , testProperty "prop_unfoldr1NM" prop_unfoldr1NM+      , testProperty "prop_constructN" prop_constructN+      , testProperty "prop_constructrN" prop_constructrN+      ]+    , testGroup "Enumeration"+      [ testProperty "prop_enumFromN" prop_enumFromN+      , testProperty "prop_enumFromN1" prop_enumFromN1+      , testProperty "prop_enumFromStepN" prop_enumFromStepN+      , testProperty "prop_enumFromStepN1" prop_enumFromStepN1+      , testProperty "prop_enumFromTo" prop_enumFromTo+      , testProperty "prop_enumFromThenTo" prop_enumFromThenTo+      ]+    , testGroup "Concatenation"+      [ testProperty "prop_cons" prop_cons+      , testProperty "prop_consV" prop_consV+      , testProperty "prop_snoc" prop_snoc+      , testProperty "prop_snocV" prop_snocV+      , testProperty "prop_append" prop_append+      , testProperty "prop_concat" prop_concat+      , testProperty "prop_concat1" prop_concat1+      ]+    , testGroup "Restricting memory usage"+      [ testProperty "prop_force" prop_force+      ]+    ]++-- ---------------------------------------------------------------------- --+-- Initialization++prop_singleton :: Int -> Property+prop_singleton a = elems (NEV.singleton a) === [a]++prop_replicate :: Int -> Int -> Property+prop_replicate n a =+    fmap elems (NEV.replicate n a) === whenNonEmpty (List.replicate n a)++prop_replicate1 :: Int -> Int -> Property+prop_replicate1 n a =+    elems (NEV.replicate1 n a) === List.replicate (max n 1) a++prop_generate :: Int -> Fun Int Int -> Property+prop_generate n (Fn f) =+    fmap elems (NEV.generate n f) === whenNonEmpty (fmap f [0 .. n - 1])++prop_generate1 :: Int -> Fun Int Int -> Property+prop_generate1 n (Fn f) =+    elems (NEV.generate1 n f) === fmap f [0 .. max n 1 - 1]++prop_iterateN :: Int -> Fun Int Int -> Int -> Property+prop_iterateN n (Fn f) a =+    fmap elems (NEV.iterateN n f a)+      === whenNonEmpty (List.take n (List.iterate f a))++prop_iterateN1 :: Int -> Fun Int Int -> Int -> Property+prop_iterateN1 n (Fn f) a =+    elems (NEV.iterateN1 n f a) === List.take (max n 1) (List.iterate f a)++-- ---------------------------------------------------------------------- --+-- Monadic initialization++prop_replicateM :: Int -> Int -> Property+prop_replicateM n a =+    fmap (fmap elems) (NEV.replicateM n (logged () a))+      === (List.replicate n (), whenNonEmpty (List.replicate n a))++prop_replicate1M :: Int -> Int -> Property+prop_replicate1M n a =+    fmap elems (NEV.replicate1M n (logged () a))+      === (List.replicate n' (), List.replicate n' a)+  where+    n' = max n 1++prop_generateM :: Int -> Fun Int Int -> Property+prop_generateM n (Fn f) =+    fmap (fmap elems) (NEV.generateM n (\i -> logged i (f i)))+      === (is, whenNonEmpty (fmap f is))+  where+    is = [0 .. n - 1]++prop_generate1M :: Int -> Fun Int Int -> Property+prop_generate1M n (Fn f) =+    fmap elems (NEV.generate1M n (\i -> logged i (f i)))+      === (is, fmap f is)+  where+    is = [0 .. max n 1 - 1]++-- The step function runs once per element after the seed.+prop_iterateNM :: Int -> Fun Int Int -> Int -> Property+prop_iterateNM n (Fn f) a =+    fmap (fmap elems) (NEV.iterateNM n (\b -> logged b (f b)) a)+      === (List.take (n - 1) as, whenNonEmpty (List.take n as))+  where+    as = List.iterate f a++prop_iterateN1M :: Int -> Fun Int Int -> Int -> Property+prop_iterateN1M n (Fn f) a =+    fmap elems (NEV.iterateN1M n (\b -> logged b (f b)) a)+      === (List.take (n' - 1) as, List.take n' as)+  where+    n' = max n 1+    as = List.iterate f a++prop_create :: [Int] -> Property+prop_create as =+    fmap elems (NEV.create (V.thaw (V.fromList as))) === whenNonEmpty as++prop_unsafeCreate :: NonEmptyVector Int -> Property+prop_unsafeCreate v =+    elems (NEV.unsafeCreate (V.thaw (V.fromList (elems v)))) === elems v++prop_createT :: [[Int]] -> Property+prop_createT ass =+    fmap (fmap elems) (NEV.createT (traverse (V.thaw . V.fromList) ass))+      === fmap whenNonEmpty ass++prop_unsafeCreateT :: [NonEmptyVector Int] -> Property+prop_unsafeCreateT vs =+    fmap elems (NEV.unsafeCreateT (traverse (V.thaw . V.fromList . elems) vs))+      === fmap elems vs++-- ---------------------------------------------------------------------- --+-- Unfolding++prop_unfoldr :: Fun Int Int -> Int -> Property+prop_unfoldr (Fn f) b =+    fmap elems (NEV.unfoldr (unfoldStep f) b)+      === whenNonEmpty (List.unfoldr (unfoldStep f) b)++prop_unfoldr1 :: Fun Int Int -> Int -> Int -> Property+prop_unfoldr1 (Fn f) a b =+    elems (NEV.unfoldr1 (unfoldStep f) a b)+      === a : List.unfoldr (unfoldStep f) b++prop_unfoldrN :: Int -> Fun Int Int -> Int -> Property+prop_unfoldrN n (Fn f) b =+    fmap elems (NEV.unfoldrN n (unfoldStep f) b)+      === whenNonEmpty (List.take n (List.unfoldr (unfoldStep f) b))++-- The initial element is supplied in addition to at most @n@ unfolded ones.+prop_unfoldr1N :: Int -> Fun Int Int -> Int -> Int -> Property+prop_unfoldr1N n (Fn f) a b =+    elems (NEV.unfoldr1N n (unfoldStep f) a b)+      === a : List.take n (List.unfoldr (unfoldStep f) b)++prop_unfoldrM :: Fun Int Int -> Int -> Property+prop_unfoldrM (Fn f) b =+    fmap (fmap elems) (NEV.unfoldrM step b)+      === fmap whenNonEmpty (unfoldrMList step b)+  where+    step s = logged s (unfoldStep f s)++prop_unfoldr1M :: Fun Int Int -> Int -> Int -> Property+prop_unfoldr1M (Fn f) a b =+    fmap elems (NEV.unfoldr1M step a b) === fmap (a :) (unfoldrMList step b)+  where+    step s = logged s (unfoldStep f s)++prop_unfoldrNM :: Int -> Fun Int Int -> Int -> Property+prop_unfoldrNM n (Fn f) b =+    fmap (fmap elems) (NEV.unfoldrNM n step b)+      === fmap whenNonEmpty (unfoldrNMList n step b)+  where+    step s = logged s (unfoldStep f s)++prop_unfoldr1NM :: Int -> Fun Int Int -> Int -> Int -> Property+prop_unfoldr1NM n (Fn f) a b =+    fmap elems (NEV.unfoldr1NM n step a b)+      === fmap (a :) (unfoldrNMList n step b)+  where+    step s = logged s (unfoldStep f s)++-- Each element is generated from the prefix constructed so far.+prop_constructN :: Fun [Int] Int -> Property+prop_constructN (Fn f) = forAll (choose (0, 30)) $ \n ->+    fmap elems (NEV.constructN n (f . V.toList))+      === whenNonEmpty (construct n [])+  where+    construct n acc+      | List.length acc >= n = acc+      | otherwise = construct n (acc ++ [f acc])++-- Each element is generated from the suffix constructed so far.+prop_constructrN :: Fun [Int] Int -> Property+prop_constructrN (Fn f) = forAll (choose (0, 30)) $ \n ->+    fmap elems (NEV.constructrN n (f . V.toList))+      === whenNonEmpty (construct n [])+  where+    construct n acc+      | List.length acc >= n = acc+      | otherwise = construct n (f acc : acc)++-- ---------------------------------------------------------------------- --+-- Enumeration++prop_enumFromN :: Int -> Int -> Property+prop_enumFromN a n =+    fmap elems (NEV.enumFromN a n) === whenNonEmpty (fmap (a +) [0 .. n - 1])++prop_enumFromN1 :: Int -> Int -> Property+prop_enumFromN1 a n =+    elems (NEV.enumFromN1 a n) === fmap (a +) [0 .. max n 1 - 1]++prop_enumFromStepN :: Int -> Int -> Int -> Property+prop_enumFromStepN a s n =+    fmap elems (NEV.enumFromStepN a s n)+      === whenNonEmpty [ a + s * i | i <- [0 .. n - 1] ]++prop_enumFromStepN1 :: Int -> Int -> Int -> Property+prop_enumFromStepN1 a s n =+    elems (NEV.enumFromStepN1 a s n) === [ a + s * i | i <- [0 .. max n 1 - 1] ]++prop_enumFromTo :: Int -> Int -> Property+prop_enumFromTo a b = fmap elems (NEV.enumFromTo a b) === whenNonEmpty [a .. b]++-- A zero step would enumerate forever.+prop_enumFromThenTo :: Int -> Int -> Int -> Property+prop_enumFromThenTo a b c = a /= b ==>+    fmap elems (NEV.enumFromThenTo a b c) === whenNonEmpty [a, b .. c]++-- ---------------------------------------------------------------------- --+-- Concatenation++prop_cons :: Int -> NonEmptyVector Int -> Property+prop_cons a v = elems (NEV.cons a v) === a : elems v++prop_consV :: Int -> [Int] -> Property+prop_consV a as = elems (NEV.consV a (V.fromList as)) === a : as++prop_snoc :: NonEmptyVector Int -> Int -> Property+prop_snoc v a = elems (NEV.snoc v a) === elems v ++ [a]++prop_snocV :: [Int] -> Int -> Property+prop_snocV as a = elems (NEV.snocV (V.fromList as) a) === as ++ [a]++prop_append :: NonEmptyVector Int -> NonEmptyVector Int -> Property+prop_append v w = elems (v NEV.++ w) === elems v ++ elems w++prop_concat :: [NonEmptyVector Int] -> Property+prop_concat vs =+    fmap elems (NEV.concat vs) === whenNonEmpty (List.concatMap elems vs)++prop_concat1 :: NonEmptyVector Int -> [NonEmptyVector Int] -> Property+prop_concat1 v vs =+    elems (NEV.concat1 (v :| vs)) === List.concatMap elems (v : vs)++-- ---------------------------------------------------------------------- --+-- Restricting memory usage++prop_force :: NonEmptyVector Int -> Property+prop_force v = forAll (genSlice v) $ \(i, m) -> m > 0 ==>+    let w = NonEmptyVector (V.slice i m (_neVec v))+    in elems (NEV.force w) === List.take m (List.drop i (elems v))
+ test/Test/Data/Vector/NonEmpty/Conversion.hs view
@@ -0,0 +1,115 @@+-- |+-- Module      : Test.Data.Vector.NonEmpty.Conversion+-- Copyright   : (c) 2019-2026 Emily Pillmore+-- License     : BSD-style+--+-- Maintainer  : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability   : experimental+-- Portability : non-portable+--+-- Properties for conversions to and from non-empty lists, vectors, and+-- lists.+--+module Test.Data.Vector.NonEmpty.Conversion+( tests+) where+++import qualified Data.List as List+import Data.List.NonEmpty (NonEmpty(..))+import qualified Data.Vector as V+import Data.Vector.NonEmpty (NonEmptyVector)+import qualified Data.Vector.NonEmpty as NEV++import Test.Data.Vector.NonEmpty.Util+import Test.QuickCheck+import Test.Tasty+import Test.Tasty.QuickCheck+++tests :: TestTree+tests = testGroup "Conversion"+    [ testGroup "To/from non-empty lists"+      [ testProperty "prop_toNonEmpty" prop_toNonEmpty+      , testProperty "prop_fromNonEmpty" prop_fromNonEmpty+      , testProperty "prop_from_to_nonempty" prop_from_to_nonempty+      , testProperty "prop_fromNonEmptyN" prop_fromNonEmptyN+      , testProperty "prop_fromNonEmptyN1" prop_fromNonEmptyN1+      , testProperty "prop_unsafeFromList" prop_unsafeFromList+      ]+    , testGroup "To/from vector"+      [ testProperty "prop_toVector" prop_toVector+      , testProperty "prop_fromVector" prop_fromVector+      , testProperty "prop_unsafeFromVector" prop_unsafeFromVector+      , testProperty "prop_from_to_vec" prop_from_to_vec+      ]+    , testGroup "To/from list"+      [ testProperty "prop_toList" prop_toList+      , testProperty "prop_fromList" prop_fromList+      , testProperty "prop_fromListN" prop_fromListN+      , testProperty "prop_from_to_list" prop_from_to_list+      ]+    ]++-- ---------------------------------------------------------------------- --+-- To/from non-empty lists++prop_toNonEmpty :: NonEmptyVector Int -> Property+prop_toNonEmpty v = NEV.toNonEmpty v === elems1 v++prop_fromNonEmpty :: Int -> [Int] -> Property+prop_fromNonEmpty a as = elems (NEV.fromNonEmpty (a :| as)) === a : as++prop_from_to_nonempty :: NonEmptyVector Int -> Property+prop_from_to_nonempty v = NEV.fromNonEmpty (NEV.toNonEmpty v) === v++prop_fromNonEmptyN :: Int -> Int -> [Int] -> Property+prop_fromNonEmptyN n a as =+    fmap elems (NEV.fromNonEmptyN n (a :| as))+      === whenNonEmpty (List.take n (a : as))++prop_fromNonEmptyN1 :: Int -> Int -> [Int] -> Property+prop_fromNonEmptyN1 n a as =+    elems (NEV.fromNonEmptyN1 n (a :| as)) === List.take (max n 1) (a : as)++prop_unsafeFromList :: NonEmptyList Int -> Property+prop_unsafeFromList (NonEmpty as) = elems (NEV.unsafeFromList as) === as++-- ---------------------------------------------------------------------- --+-- To/from vector++prop_toVector :: NonEmptyVector Int -> Property+prop_toVector v = V.toList (NEV.toVector v) === elems v++prop_fromVector :: [Int] -> Property+prop_fromVector as =+    fmap elems (NEV.fromVector (V.fromList as)) === whenNonEmpty as++prop_unsafeFromVector :: NonEmptyList Int -> Property+prop_unsafeFromVector (NonEmpty as) =+    elems (NEV.unsafeFromVector (V.fromList as)) === as++prop_from_to_vec :: NonEmptyVector Int -> NonEmptyList Int -> Property+prop_from_to_vec t (NonEmpty as) =+    NEV.fromVector (NEV.toVector t) === Just t+      .&&. (NEV.toVector <$> NEV.fromVector u) === Just u+  where+    u = V.fromList as++-- ---------------------------------------------------------------------- --+-- To/from list++prop_toList :: NonEmptyVector Int -> Property+prop_toList v = NEV.toList v === elems v++prop_fromList :: [Int] -> Property+prop_fromList as = fmap elems (NEV.fromList as) === whenNonEmpty as++prop_fromListN :: Int -> [Int] -> Property+prop_fromListN n as =+    fmap elems (NEV.fromListN n as) === whenNonEmpty (List.take n as)++prop_from_to_list :: NonEmptyVector Int -> NonEmptyList Int -> Property+prop_from_to_list t (NonEmpty u) =+    NEV.fromList (NEV.toList t) === Just t+      .&&. fmap NEV.toList (NEV.fromList u) === Just u
+ test/Test/Data/Vector/NonEmpty/Elementwise.hs view
@@ -0,0 +1,284 @@+-- |+-- Module      : Test.Data.Vector.NonEmpty.Elementwise+-- Copyright   : (c) 2019-2026 Emily Pillmore+-- License     : BSD-style+--+-- Maintainer  : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability   : experimental+-- Portability : non-portable+--+-- Properties for indexing, mapping, zipping, and unzipping.+--+-- Monadic operations run in a writer-style @([w], -)@ monad so that the+-- order and number of effects are observable.+--+module Test.Data.Vector.NonEmpty.Elementwise+( tests+) where+++import qualified Data.List as List+import Data.Vector.NonEmpty (NonEmptyVector)+import qualified Data.Vector.NonEmpty as NEV++import Test.Data.Vector.NonEmpty.Util+import Test.QuickCheck+import Test.Tasty+import Test.Tasty.QuickCheck+++tests :: TestTree+tests = testGroup "Elementwise"+    [ testGroup "Indexing"+      [ testProperty "prop_indexed" prop_indexed+      ]+    , testGroup "Mapping"+      [ testProperty "prop_map" prop_map+      , testProperty "prop_imap" prop_imap+      , testProperty "prop_concatMap" prop_concatMap+      ]+    , testGroup "Monadic mapping"+      [ testProperty "prop_mapM" prop_mapM+      , testProperty "prop_imapM" prop_imapM+      , testProperty "prop_mapM_" prop_mapM_+      , testProperty "prop_imapM_" prop_imapM_+      , testProperty "prop_forM" prop_forM+      , testProperty "prop_forM_" prop_forM_+      ]+    , testGroup "Zipping"+      [ testProperty "prop_zipWith" prop_zipWith+      , testProperty "prop_zipWith3" prop_zipWith3+      , testProperty "prop_zipWith4" prop_zipWith4+      , testProperty "prop_zipWith5" prop_zipWith5+      , testProperty "prop_zipWith6" prop_zipWith6+      , testProperty "prop_izipWith" prop_izipWith+      , testProperty "prop_izipWith3" prop_izipWith3+      , testProperty "prop_izipWith4" prop_izipWith4+      , testProperty "prop_izipWith5" prop_izipWith5+      , testProperty "prop_izipWith6" prop_izipWith6+      , testProperty "prop_zip" prop_zip+      , testProperty "prop_zip3" prop_zip3+      , testProperty "prop_zip4" prop_zip4+      , testProperty "prop_zip5" prop_zip5+      , testProperty "prop_zip6" prop_zip6+      ]+    , testGroup "Monadic zipping"+      [ testProperty "prop_zipWithM" prop_zipWithM+      , testProperty "prop_zipWithM_" prop_zipWithM_+      , testProperty "prop_izipWithM" prop_izipWithM+      , testProperty "prop_izipWithM_" prop_izipWithM_+      ]+    , testGroup "Unzipping"+      [ testProperty "prop_unzip" prop_unzip+      , testProperty "prop_unzip3" prop_unzip3+      , testProperty "prop_unzip4" prop_unzip4+      , testProperty "prop_unzip5" prop_unzip5+      , testProperty "prop_unzip6" prop_unzip6+      ]+    ]++type V = NonEmptyVector Int++-- ---------------------------------------------------------------------- --+-- Indexing++prop_indexed :: V -> Property+prop_indexed v = elems (NEV.indexed v) === List.zip [0 ..] (elems v)++-- ---------------------------------------------------------------------- --+-- Mapping++prop_map :: Fun Int Int -> V -> Property+prop_map (Fn f) v = elems (NEV.map f v) === List.map f (elems v)++prop_imap :: Fun (Int, Int) Int -> V -> Property+prop_imap (Fn2 f) v = elems (NEV.imap f v) === List.zipWith f [0 ..] (elems v)++prop_concatMap :: Fun Int V -> V -> Property+prop_concatMap (Fn f) v =+    elems (NEV.concatMap f v) === List.concatMap (elems . f) (elems v)++-- ---------------------------------------------------------------------- --+-- Monadic mapping++prop_mapM :: Fun Int Int -> V -> Property+prop_mapM (Fn f) v =+    fmap elems (NEV.mapM (\a -> logged a (f a)) v)+      === (elems v, List.map f (elems v))++prop_imapM :: Fun (Int, Int) Int -> V -> Property+prop_imapM (Fn2 f) v =+    fmap elems (NEV.imapM (\i a -> logged i (f i a)) v)+      === (is, List.zipWith f is (elems v))+  where+    is = [0 .. NEV.length v - 1]++prop_mapM_ :: V -> Property+prop_mapM_ v = NEV.mapM_ (\a -> logged a a) v === (elems v, ())++prop_imapM_ :: V -> Property+prop_imapM_ v =+    NEV.imapM_ (\i a -> logged (i, a) a) v+      === (List.zip [0 ..] (elems v), ())++prop_forM :: Fun Int Int -> V -> Property+prop_forM (Fn f) v =+    fmap elems (NEV.forM v (\a -> logged a (f a)))+      === (elems v, List.map f (elems v))++prop_forM_ :: V -> Property+prop_forM_ v = NEV.forM_ v (\a -> logged a a) === (elems v, ())++-- ---------------------------------------------------------------------- --+-- Zipping+--+-- The higher arities collect their arguments in order, so the models+-- detect both truncation to the shortest input and argument order.++prop_zipWith :: Fun (Int, Int) Int -> V -> V -> Property+prop_zipWith (Fn2 f) a b =+    elems (NEV.zipWith f a b) === List.zipWith f (elems a) (elems b)++prop_zipWith3 :: Fun (Int, Int, Int) Int -> V -> V -> V -> Property+prop_zipWith3 (Fn3 f) a b c =+    elems (NEV.zipWith3 f a b c)+      === List.zipWith3 f (elems a) (elems b) (elems c)++prop_zipWith4 :: V -> V -> V -> V -> Property+prop_zipWith4 a b c d =+    elems (NEV.zipWith4 k a b c d)+      === List.zipWith4 k (elems a) (elems b) (elems c) (elems d)+  where+    k p q r s = [p, q, r, s]++prop_zipWith5 :: V -> V -> V -> V -> V -> Property+prop_zipWith5 a b c d e =+    elems (NEV.zipWith5 k a b c d e)+      === List.zipWith5 k (elems a) (elems b) (elems c) (elems d) (elems e)+  where+    k p q r s t = [p, q, r, s, t]++prop_zipWith6 :: V -> V -> V -> V -> V -> V -> Property+prop_zipWith6 a b c d e f =+    elems (NEV.zipWith6 k a b c d e f)+      === List.zipWith6 k+            (elems a) (elems b) (elems c) (elems d) (elems e) (elems f)+  where+    k p q r s t u = [p, q, r, s, t, u]++prop_izipWith :: V -> V -> Property+prop_izipWith a b =+    elems (NEV.izipWith k a b) === List.zipWith3 k [0 ..] (elems a) (elems b)+  where+    k i p q = [i, p, q]++prop_izipWith3 :: V -> V -> V -> Property+prop_izipWith3 a b c =+    elems (NEV.izipWith3 k a b c)+      === List.zipWith4 k [0 ..] (elems a) (elems b) (elems c)+  where+    k i p q r = [i, p, q, r]++prop_izipWith4 :: V -> V -> V -> V -> Property+prop_izipWith4 a b c d =+    elems (NEV.izipWith4 k a b c d)+      === List.zipWith5 k [0 ..] (elems a) (elems b) (elems c) (elems d)+  where+    k i p q r s = [i, p, q, r, s]++prop_izipWith5 :: V -> V -> V -> V -> V -> Property+prop_izipWith5 a b c d e =+    elems (NEV.izipWith5 k a b c d e)+      === List.zipWith6 k [0 ..]+            (elems a) (elems b) (elems c) (elems d) (elems e)+  where+    k i p q r s t = [i, p, q, r, s, t]++prop_izipWith6 :: V -> V -> V -> V -> V -> V -> Property+prop_izipWith6 a b c d e f =+    elems (NEV.izipWith6 k a b c d e f)+      === List.zipWith7 k [0 ..]+            (elems a) (elems b) (elems c) (elems d) (elems e) (elems f)+  where+    k i p q r s t u = [i, p, q, r, s, t, u]++prop_zip :: V -> V -> Property+prop_zip a b = elems (NEV.zip a b) === List.zip (elems a) (elems b)++prop_zip3 :: V -> V -> V -> Property+prop_zip3 a b c =+    elems (NEV.zip3 a b c) === List.zip3 (elems a) (elems b) (elems c)++prop_zip4 :: V -> V -> V -> V -> Property+prop_zip4 a b c d =+    elems (NEV.zip4 a b c d)+      === List.zip4 (elems a) (elems b) (elems c) (elems d)++prop_zip5 :: V -> V -> V -> V -> V -> Property+prop_zip5 a b c d e =+    elems (NEV.zip5 a b c d e)+      === List.zip5 (elems a) (elems b) (elems c) (elems d) (elems e)++prop_zip6 :: V -> V -> V -> V -> V -> V -> Property+prop_zip6 a b c d e f =+    elems (NEV.zip6 a b c d e f)+      === List.zip6 (elems a) (elems b) (elems c) (elems d) (elems e) (elems f)++-- ---------------------------------------------------------------------- --+-- Monadic zipping++prop_zipWithM :: Fun (Int, Int) Int -> V -> V -> Property+prop_zipWithM (Fn2 f) a b =+    fmap elems (NEV.zipWithM (\p q -> logged (p, q) (f p q)) a b)+      === (ps, List.map (uncurry f) ps)+  where+    ps = List.zip (elems a) (elems b)++prop_zipWithM_ :: V -> V -> Property+prop_zipWithM_ a b =+    NEV.zipWithM_ (\p q -> logged (p, q) p) a b+      === (List.zip (elems a) (elems b), ())++prop_izipWithM :: Fun (Int, Int, Int) Int -> V -> V -> Property+prop_izipWithM (Fn3 f) a b =+    fmap elems (NEV.izipWithM (\i p q -> logged i (f i p q)) a b)+      === (List.map fst ips, List.map (\(i, (p, q)) -> f i p q) ips)+  where+    ips = List.zip [0 ..] (List.zip (elems a) (elems b))++prop_izipWithM_ :: V -> V -> Property+prop_izipWithM_ a b =+    NEV.izipWithM_ (\i p q -> logged (i, p, q) p) a b+      === (List.zip3 [0 ..] (elems a) (elems b), ())++-- ---------------------------------------------------------------------- --+-- Unzipping++prop_unzip :: NonEmptyVector (Int, Int) -> Property+prop_unzip v = (elems a, elems b) === List.unzip (elems v)+  where+    (a, b) = NEV.unzip v++prop_unzip3 :: NonEmptyVector (Int, Int, Int) -> Property+prop_unzip3 v = (elems a, elems b, elems c) === List.unzip3 (elems v)+  where+    (a, b, c) = NEV.unzip3 v++prop_unzip4 :: NonEmptyVector (Int, Int, Int, Int) -> Property+prop_unzip4 v =+    (elems a, elems b, elems c, elems d) === List.unzip4 (elems v)+  where+    (a, b, c, d) = NEV.unzip4 v++prop_unzip5 :: NonEmptyVector (Int, Int, Int, Int, Int) -> Property+prop_unzip5 v =+    (elems a, elems b, elems c, elems d, elems e) === List.unzip5 (elems v)+  where+    (a, b, c, d, e) = NEV.unzip5 v++prop_unzip6 :: NonEmptyVector (Int, Int, Int, Int, Int, Int) -> Property+prop_unzip6 v =+    (elems a, elems b, elems c, elems d, elems e, elems f)+      === List.unzip6 (elems v)+  where+    (a, b, c, d, e, f) = NEV.unzip6 v
+ test/Test/Data/Vector/NonEmpty/Folds.hs view
@@ -0,0 +1,309 @@+-- |+-- Module      : Test.Data.Vector.NonEmpty.Folds+-- Copyright   : (c) 2019-2026 Emily Pillmore+-- License     : BSD-style+--+-- Maintainer  : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability   : experimental+-- Portability : non-portable+--+-- Properties for folds, specialized folds, monadic folds, and sequencing.+--+-- Fold functions are arbitrary, hence neither associative nor commutative,+-- so the models pin down fold direction and argument order. Monadic folds+-- run in a writer-style @([w], -)@ monad to expose effect order.+--+module Test.Data.Vector.NonEmpty.Folds+( tests+) where+++import qualified Control.Monad as Monad+import qualified Data.List as List+import Data.List.NonEmpty (NonEmpty(..))+import Data.Ord (comparing)+import Data.Vector.NonEmpty (NonEmptyVector)+import qualified Data.Vector.NonEmpty as NEV++import Test.Data.Vector.NonEmpty.Util+import Test.QuickCheck+import Test.Tasty+import Test.Tasty.QuickCheck+++tests :: TestTree+tests = testGroup "Folds"+    [ testGroup "Folding"+      [ testProperty "prop_foldl" prop_foldl+      , testProperty "prop_foldl1" prop_foldl1+      , testProperty "prop_foldl'" prop_foldl'+      , testProperty "prop_foldl1'" prop_foldl1'+      , testProperty "prop_foldr" prop_foldr+      , testProperty "prop_foldr1" prop_foldr1+      , testProperty "prop_foldr'" prop_foldr'+      , testProperty "prop_foldr1'" prop_foldr1'+      , testProperty "prop_ifoldl" prop_ifoldl+      , testProperty "prop_ifoldl'" prop_ifoldl'+      , testProperty "prop_ifoldr" prop_ifoldr+      , testProperty "prop_ifoldr'" prop_ifoldr'+      ]+    , testGroup "Specialized folds"+      [ testProperty "prop_all" prop_all+      , testProperty "prop_any" prop_any+      , testProperty "prop_and" prop_and+      , testProperty "prop_or" prop_or+      , testProperty "prop_sum" prop_sum+      , testProperty "prop_product" prop_product+      , testProperty "prop_maximum" prop_maximum+      , testProperty "prop_maximumBy" prop_maximumBy+      , testProperty "prop_minimum" prop_minimum+      , testProperty "prop_minimumBy" prop_minimumBy+      , testProperty "prop_maxIndex" prop_maxIndex+      , testProperty "prop_maxIndexBy" prop_maxIndexBy+      , testProperty "prop_minIndex" prop_minIndex+      , testProperty "prop_minIndexBy" prop_minIndexBy+      ]+    , testGroup "Monadic folds"+      [ testProperty "prop_foldM" prop_foldM+      , testProperty "prop_foldM'" prop_foldM'+      , testProperty "prop_fold1M" prop_fold1M+      , testProperty "prop_fold1M'" prop_fold1M'+      , testProperty "prop_foldM_" prop_foldM_+      , testProperty "prop_foldM'_" prop_foldM'_+      , testProperty "prop_fold1M_" prop_fold1M_+      , testProperty "prop_fold1M'_" prop_fold1M'_+      , testProperty "prop_ifoldM" prop_ifoldM+      , testProperty "prop_ifoldM'" prop_ifoldM'+      , testProperty "prop_ifoldM_" prop_ifoldM_+      , testProperty "prop_ifoldM'_" prop_ifoldM'_+      ]+    , testGroup "Monadic sequencing"+      [ testProperty "prop_sequence" prop_sequence+      , testProperty "prop_sequence_short_circuits" prop_sequence_short_circuits+      , testProperty "prop_sequence_" prop_sequence_+      ]+    ]++type V = NonEmptyVector Int++-- ---------------------------------------------------------------------- --+-- Models++indexedList :: V -> [(Int, Int)]+indexedList = List.zip [0 ..] . elems++-- | Ties resolve to the first occurrence, matching @vector@.+--+firstBy :: (a -> a -> Bool) -> [(Int, a)] -> (Int, a)+firstBy better = List.foldl1 (\x y -> if better (snd y) (snd x) then y else x)++-- | A comparison with many ties, so that tie-breaking is exercised.+--+coarse :: Int -> Int -> Ordering+coarse = comparing (`mod` 3)++-- | A writer-style step that logs the accumulator and element it consumes,+-- exposing how the accumulator is threaded even when the result is+-- discarded.+--+step :: (Int -> Int -> Int) -> Int -> Int -> ([(Int, Int)], Int)+step f acc a = logged (acc, a) (f acc a)++-- | A writer-style indexed step that logs the accumulator and index it+-- consumes.+--+istep+    :: (Int -> Int -> Int -> Int)+    -> Int+    -> Int+    -> Int+    -> ([(Int, Int)], Int)+istep f acc i a = logged (acc, i) (f acc i a)++-- ---------------------------------------------------------------------- --+-- Folding++prop_foldl :: Fun (Int, Int) Int -> Int -> V -> Property+prop_foldl (Fn2 f) z v = NEV.foldl f z v === List.foldl f z (elems v)++prop_foldl1 :: Fun (Int, Int) Int -> V -> Property+prop_foldl1 (Fn2 f) v = NEV.foldl1 f v === foldl1 f (elems1 v)++prop_foldl' :: Fun (Int, Int) Int -> Int -> V -> Property+prop_foldl' (Fn2 f) z v = NEV.foldl' f z v === List.foldl f z (elems v)++prop_foldl1' :: Fun (Int, Int) Int -> V -> Property+prop_foldl1' (Fn2 f) v = NEV.foldl1' f v === foldl1 f (elems1 v)++prop_foldr :: Fun (Int, Int) Int -> Int -> V -> Property+prop_foldr (Fn2 f) z v = NEV.foldr f z v === List.foldr f z (elems v)++prop_foldr1 :: Fun (Int, Int) Int -> V -> Property+prop_foldr1 (Fn2 f) v = NEV.foldr1 f v === foldr1 f (elems1 v)++prop_foldr' :: Fun (Int, Int) Int -> Int -> V -> Property+prop_foldr' (Fn2 f) z v = NEV.foldr' f z v === List.foldr f z (elems v)++prop_foldr1' :: Fun (Int, Int) Int -> V -> Property+prop_foldr1' (Fn2 f) v = NEV.foldr1' f v === foldr1 f (elems1 v)++prop_ifoldl :: Fun (Int, Int, Int) Int -> Int -> V -> Property+prop_ifoldl (Fn3 f) z v =+    NEV.ifoldl f z v+      === List.foldl (\acc (i, a) -> f acc i a) z (indexedList v)++prop_ifoldl' :: Fun (Int, Int, Int) Int -> Int -> V -> Property+prop_ifoldl' (Fn3 f) z v =+    NEV.ifoldl' f z v+      === List.foldl (\acc (i, a) -> f acc i a) z (indexedList v)++prop_ifoldr :: Fun (Int, Int, Int) Int -> Int -> V -> Property+prop_ifoldr (Fn3 f) z v =+    NEV.ifoldr f z v+      === List.foldr (\(i, a) acc -> f i a acc) z (indexedList v)++prop_ifoldr' :: Fun (Int, Int, Int) Int -> Int -> V -> Property+prop_ifoldr' (Fn3 f) z v =+    NEV.ifoldr' f z v+      === List.foldr (\(i, a) acc -> f i a acc) z (indexedList v)++-- ---------------------------------------------------------------------- --+-- Specialized folds++prop_all :: Fun Int Bool -> V -> Property+prop_all (Fn p) v = NEV.all p v === List.all p (elems v)++prop_any :: Fun Int Bool -> V -> Property+prop_any (Fn p) v = NEV.any p v === List.any p (elems v)++prop_and :: NonEmptyVector Bool -> Property+prop_and v = NEV.and v === List.and (elems v)++prop_or :: NonEmptyVector Bool -> Property+prop_or v = NEV.or v === List.or (elems v)++prop_sum :: V -> Property+prop_sum v = NEV.sum v === List.sum (elems v)++prop_product :: V -> Property+prop_product v = NEV.product v === List.product (elems v)++prop_maximum :: V -> Property+prop_maximum v = NEV.maximum v === maximum (elems1 v)++prop_maximumBy :: V -> Property+prop_maximumBy v =+    NEV.maximumBy coarse v+      === snd (firstBy (\a b -> coarse a b == GT) (indexedList v))++prop_minimum :: V -> Property+prop_minimum v = NEV.minimum v === minimum (elems1 v)++prop_minimumBy :: V -> Property+prop_minimumBy v =+    NEV.minimumBy coarse v+      === snd (firstBy (\a b -> coarse a b == LT) (indexedList v))++prop_maxIndex :: V -> Property+prop_maxIndex v =+    NEV.maxIndex w === fst (firstBy (>) (indexedList w))+  where+    w = vec (List.map (`mod` 4) (elems v))++prop_maxIndexBy :: V -> Property+prop_maxIndexBy v =+    NEV.maxIndexBy coarse v+      === fst (firstBy (\a b -> coarse a b == GT) (indexedList v))++prop_minIndex :: V -> Property+prop_minIndex v =+    NEV.minIndex w === fst (firstBy (<) (indexedList w))+  where+    w = vec (List.map (`mod` 4) (elems v))++prop_minIndexBy :: V -> Property+prop_minIndexBy v =+    NEV.minIndexBy coarse v+      === fst (firstBy (\a b -> coarse a b == LT) (indexedList v))++-- ---------------------------------------------------------------------- --+-- Monadic folds++prop_foldM :: Fun (Int, Int) Int -> Int -> V -> Property+prop_foldM (Fn2 f) z v =+    NEV.foldM (step f) z v === Monad.foldM (step f) z (elems v)++prop_foldM' :: Fun (Int, Int) Int -> Int -> V -> Property+prop_foldM' (Fn2 f) z v =+    NEV.foldM' (step f) z v === Monad.foldM (step f) z (elems v)++prop_fold1M :: Fun (Int, Int) Int -> V -> Property+prop_fold1M (Fn2 f) v =+    NEV.fold1M (step f) v === Monad.foldM (step f) a as+  where+    a :| as = elems1 v++prop_fold1M' :: Fun (Int, Int) Int -> V -> Property+prop_fold1M' (Fn2 f) v =+    NEV.fold1M' (step f) v === Monad.foldM (step f) a as+  where+    a :| as = elems1 v++prop_foldM_ :: Fun (Int, Int) Int -> Int -> V -> Property+prop_foldM_ (Fn2 f) z v =+    NEV.foldM_ (step f) z v === Monad.foldM_ (step f) z (elems v)++prop_foldM'_ :: Fun (Int, Int) Int -> Int -> V -> Property+prop_foldM'_ (Fn2 f) z v =+    NEV.foldM'_ (step f) z v === Monad.foldM_ (step f) z (elems v)++prop_fold1M_ :: Fun (Int, Int) Int -> V -> Property+prop_fold1M_ (Fn2 f) v =+    NEV.fold1M_ (step f) v === Monad.foldM_ (step f) a as+  where+    a :| as = elems1 v++prop_fold1M'_ :: Fun (Int, Int) Int -> V -> Property+prop_fold1M'_ (Fn2 f) v =+    NEV.fold1M'_ (step f) v === Monad.foldM_ (step f) a as+  where+    a :| as = elems1 v++prop_ifoldM :: Fun (Int, Int, Int) Int -> Int -> V -> Property+prop_ifoldM (Fn3 f) z v =+    NEV.ifoldM (istep f) z v+      === Monad.foldM (\acc (i, a) -> istep f acc i a) z (indexedList v)++prop_ifoldM' :: Fun (Int, Int, Int) Int -> Int -> V -> Property+prop_ifoldM' (Fn3 f) z v =+    NEV.ifoldM' (istep f) z v+      === Monad.foldM (\acc (i, a) -> istep f acc i a) z (indexedList v)++prop_ifoldM_ :: Fun (Int, Int, Int) Int -> Int -> V -> Property+prop_ifoldM_ (Fn3 f) z v =+    NEV.ifoldM_ (istep f) z v+      === Monad.foldM_ (\acc (i, a) -> istep f acc i a) z (indexedList v)++prop_ifoldM'_ :: Fun (Int, Int, Int) Int -> Int -> V -> Property+prop_ifoldM'_ (Fn3 f) z v =+    NEV.ifoldM'_ (istep f) z v+      === Monad.foldM_ (\acc (i, a) -> istep f acc i a) z (indexedList v)++-- ---------------------------------------------------------------------- --+-- Monadic sequencing++prop_sequence :: NonEmptyVector (Int, Int) -> Property+prop_sequence v =+    fmap elems (NEV.sequence (vec (List.map (uncurry logged) (elems v))))+      === List.unzip (elems v)++prop_sequence_short_circuits :: V -> Property+prop_sequence_short_circuits v = forAll (genIndex v) $ \i ->+    let ms = List.map Just (elems v)+    in fmap elems (NEV.sequence (vec ms)) === Just (elems v)+         .&&. NEV.sequence (vec (adjustAt i (const Nothing) ms)) === Nothing++prop_sequence_ :: NonEmptyVector (Int, Int) -> Property+prop_sequence_ v =+    NEV.sequence_ (vec (List.map (uncurry logged) (elems v)))+      === (List.map fst (elems v), ())
+ test/Test/Data/Vector/NonEmpty/Grouping.hs view
@@ -0,0 +1,106 @@+module Test.Data.Vector.NonEmpty.Grouping+( tests+) where+++import qualified Data.List as List+import qualified Data.List.NonEmpty as NonEmpty+import Data.Vector.NonEmpty (NonEmptyVector)+import qualified Data.Vector.NonEmpty as NEV++import Test.Data.Vector.NonEmpty.Util+import Test.QuickCheck+import Test.Tasty+import Test.Tasty.QuickCheck+++tests :: TestTree+tests = testGroup "Grouping"+    [ testGroup "group"+      [ testProperty "prop_group" prop_group+      , testProperty "prop_group_singleton" prop_group_singleton+      , testProperty "prop_group_all_equal" prop_group_all_equal+      , testProperty "prop_group_distinct" prop_group_distinct+      , testProperty "prop_group_runs" prop_group_runs+      ]+    , testGroup "groupBy"+      [ testProperty "prop_groupBy" prop_groupBy+      , testProperty "prop_groupBy_singleton" prop_groupBy_singleton+      , testProperty "prop_groupBy_all_true" prop_groupBy_all_true+      , testProperty "prop_groupBy_all_false" prop_groupBy_all_false+      , testProperty "prop_groupBy_equivalence" prop_groupBy_equivalence+      , testProperty "prop_groupBy_nontransitive" prop_groupBy_nontransitive+      , testProperty "prop_groupBy_asymmetric" prop_groupBy_asymmetric+      ]+    ]++groupElems :: NonEmpty.NonEmpty (NonEmptyVector element) -> [[element]]+groupElems = List.map elems . NonEmpty.toList++prop_group :: NonEmptyVector Int -> Property+prop_group input =+    groupElems (NEV.group (vec values)) === List.group values+  where+    values = List.map (`mod` 4) (elems input)++prop_group_singleton :: Int -> Property+prop_group_singleton value =+    groupElems (NEV.group (vec [value])) === [[value]]++prop_group_all_equal :: Int -> Positive Int -> Property+prop_group_all_equal value (Positive count) =+    groupElems (NEV.group (vec values)) === [values]+  where+    values = List.replicate count value++prop_group_distinct :: NonEmptyVector Int -> Property+prop_group_distinct input =+    groupElems (NEV.group (vec values)) === List.map (: []) values+  where+    values = List.nub (elems input)++prop_group_runs :: Property+prop_group_runs = once $+    groupElems (NEV.group (vec "Mississippi"))+      === ["M", "i", "ss", "i", "ss", "i", "pp", "i"]++prop_groupBy :: Fun (Int, Int) Bool -> NonEmptyVector Int -> Property+prop_groupBy (Fn2 predicate) input =+    groupElems (NEV.groupBy predicate (vec values))+      === List.groupBy predicate values+  where+    values = List.map (`mod` 4) (elems input)++prop_groupBy_singleton :: Int -> Property+prop_groupBy_singleton value =+    groupElems (NEV.groupBy predicate (vec [value])) === [[value]]+  where+    predicate _ _ = error "groupBy evaluated its predicate on a singleton"++prop_groupBy_all_true :: NonEmptyVector Int -> Property+prop_groupBy_all_true input =+    groupElems (NEV.groupBy (\_ _ -> True) input) === [elems input]++prop_groupBy_all_false :: NonEmptyVector Int -> Property+prop_groupBy_all_false input =+    groupElems (NEV.groupBy (\_ _ -> False) input)+      === List.map (: []) (elems input)++prop_groupBy_equivalence :: NonEmptyVector Int -> Property+prop_groupBy_equivalence input =+    groupElems (NEV.groupBy sameParity input)+      === List.groupBy sameParity (elems input)+  where+    sameParity first second = even first == even second++prop_groupBy_nontransitive :: Property+prop_groupBy_nontransitive = once $+    groupElems (NEV.groupBy nearby (vec [1, 2, 3, 4] :: NonEmptyVector Int))+      === [[1, 2], [3, 4]]+  where+    nearby first second = abs (first - second) <= 1++prop_groupBy_asymmetric :: Property+prop_groupBy_asymmetric = once $+    groupElems (NEV.groupBy (<) (vec [1, 2, 1, 3, 0, 4] :: NonEmptyVector Int))+      === [[1, 2], [1, 3], [0, 4]]
+ test/Test/Data/Vector/NonEmpty/Instances.hs view
@@ -0,0 +1,435 @@+{-# LANGUAGE CPP #-}+-- |+-- Module      : Test.Data.Vector.NonEmpty.Instances+-- Copyright   : (c) 2019-2026 Emily Pillmore+-- License     : BSD-style+--+-- Maintainer  : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability   : experimental+-- Portability : non-portable+--+-- Laws and list models for the type class instances of t'NonEmptyVector'.+--+module Test.Data.Vector.NonEmpty.Instances+( tests+) where+++import qualified Control.Applicative as Applicative+import Control.DeepSeq (rnf)+import Control.Exception (evaluate)+import Control.Monad.Zip (munzip, mzip, mzipWith)++import Data.Data (gmapM, gmapQ, gmapT, showConstr, toConstr)+import qualified Data.Foldable as Foldable+#if MIN_VERSION_base(4,18,0)+import qualified Data.Foldable1 as Foldable1+#endif+import Data.Functor.Classes (liftCompare, liftEq, readsPrec1, showsPrec1)+import Data.Functor.Compose (Compose(..))+import Data.Functor.Identity (Identity(..))+import qualified Data.List as List+import Data.List.NonEmpty (NonEmpty(..))+import Data.Ord (comparing)+import Data.Semigroup (sconcat, stimes)+import Data.Vector.NonEmpty (NonEmptyVector)+import qualified Data.Vector.NonEmpty as NEV++import Test.Data.Vector.NonEmpty.Util+import Test.QuickCheck+import Test.Tasty+import Test.Tasty.QuickCheck++import Text.Read (readMaybe)+++tests :: TestTree+tests = testGroup "Instances"+    [ testGroup "Eq"+      [ testProperty "prop_eq" prop_eq+      , testProperty "prop_liftEq" prop_liftEq+      ]+    , testGroup "Ord"+      [ testProperty "prop_compare" prop_compare+      , testProperty "prop_liftCompare" prop_liftCompare+      ]+    , testGroup "Show"+      [ testProperty "prop_show" prop_show+      , testProperty "prop_showsPrec" prop_showsPrec+      , testProperty "prop_showsPrec1" prop_showsPrec1+      ]+    , testGroup "Read"+      [ testProperty "prop_read_show" prop_read_show+      , testProperty "prop_read_list" prop_read_list+      , testProperty "prop_readsPrec1" prop_readsPrec1+      ]+    , testGroup "Functor"+      [ testProperty "prop_fmap" prop_fmap+      , testProperty "prop_functor_identity" prop_functor_identity+      , testProperty "prop_functor_composition" prop_functor_composition+      ]+    , testGroup "Applicative"+      [ testProperty "prop_pure" prop_pure+      , testProperty "prop_ap" prop_ap+      , testProperty "prop_liftA2" prop_liftA2+      , testProperty "prop_then" prop_then+      , testProperty "prop_before" prop_before+      , testProperty "prop_applicative_identity" prop_applicative_identity+      , testProperty "prop_applicative_homomorphism" prop_applicative_homomorphism+      , testProperty "prop_applicative_interchange" prop_applicative_interchange+      , testProperty "prop_applicative_composition" prop_applicative_composition+      ]+    , testGroup "Monad"+      [ testProperty "prop_bind" prop_bind+      , testProperty "prop_monad_left_identity" prop_monad_left_identity+      , testProperty "prop_monad_right_identity" prop_monad_right_identity+      , testProperty "prop_monad_associativity" prop_monad_associativity+      ]+    , testGroup "MonadZip"+      [ testProperty "prop_mzip" prop_mzip+      , testProperty "prop_mzipWith" prop_mzipWith+      , testProperty "prop_munzip" prop_munzip+      , testProperty "prop_monadzip_information" prop_monadzip_information+      ]+    , testGroup "Semigroup"+      [ testProperty "prop_append" prop_append+      , testProperty "prop_semigroup_associativity" prop_semigroup_associativity+      , testProperty "prop_sconcat" prop_sconcat+      , testProperty "prop_stimes" prop_stimes+      , testProperty "prop_stimes_non_positive" prop_stimes_non_positive+      ]+    , testGroup "Foldable"+      [ testProperty "prop_foldable_toList" prop_foldable_toList+      , testProperty "prop_foldable_foldMap" prop_foldable_foldMap+      , testProperty "prop_foldable_foldr" prop_foldable_foldr+      , testProperty "prop_foldable_foldl" prop_foldable_foldl+      , testProperty "prop_foldable_length" prop_foldable_length+      , testProperty "prop_foldable_null" prop_foldable_null+      , testProperty "prop_foldable_elem" prop_foldable_elem+      , testProperty "prop_foldable_sum_product" prop_foldable_sum_product+      , testProperty "prop_foldable_maximum_minimum" prop_foldable_maximum_minimum+      ]+#if MIN_VERSION_base(4,18,0)+    , testGroup "Foldable1"+      [ testProperty "prop_foldable1_foldMap1" prop_foldable1_foldMap1+      , testProperty "prop_foldable1_foldMap1'" prop_foldable1_foldMap1'+      , testProperty "prop_foldable1_fold1" prop_foldable1_fold1+      , testProperty "prop_foldable1_toNonEmpty" prop_foldable1_toNonEmpty+      , testProperty "prop_foldable1_head_last" prop_foldable1_head_last+      , testProperty "prop_foldable1_maximum_minimum" prop_foldable1_maximum_minimum+      , testProperty "prop_foldable1_foldrMap1" prop_foldable1_foldrMap1+      , testProperty "prop_foldable1_foldlMap1" prop_foldable1_foldlMap1+      ]+#endif+    , testGroup "Traversable"+      [ testProperty "prop_traverse" prop_traverse+      , testProperty "prop_sequenceA" prop_sequenceA+      , testProperty "prop_traversable_identity" prop_traversable_identity+      , testProperty "prop_traversable_composition" prop_traversable_composition+      ]+    , testGroup "NFData"+      [ testProperty "prop_rnf" prop_rnf+      , testProperty "prop_rnf_forces_elements" prop_rnf_forces_elements+      ]+    , testGroup "Data"+      [ testProperty "prop_data" prop_data+      ]+    ]++type V = NonEmptyVector Int++-- | Either the same vector or an unrelated one, so that equal pairs are+-- generated often.+--+genNear :: V -> Gen V+genNear v = oneof [pure v, arbitrary]++-- ---------------------------------------------------------------------- --+-- Eq and Ord++prop_eq :: V -> Property+prop_eq v = forAll (genNear v) $ \w -> (v == w) === (elems v == elems w)++prop_liftEq :: V -> Property+prop_liftEq v = forAll (genNear v) $ \w ->+    liftEq eqMod3 v w === liftEq eqMod3 (elems v) (elems w)+  where+    eqMod3 a b = a `mod` 3 == b `mod` 3++prop_compare :: V -> Property+prop_compare v = forAll (genNear v) $ \w ->+    compare v w === compare (elems v) (elems w)++prop_liftCompare :: V -> Property+prop_liftCompare v = forAll (genNear v) $ \w ->+    liftCompare cmp v w === liftCompare cmp (elems v) (elems w)+  where+    cmp = comparing (`mod` 3)++-- ---------------------------------------------------------------------- --+-- Show and Read++prop_show :: V -> Property+prop_show v = show v === show (elems v)++prop_showsPrec :: Int -> V -> String -> Property+prop_showsPrec d v s = showsPrec d v s === show (elems v) ++ s++prop_showsPrec1 :: Int -> V -> String -> Property+prop_showsPrec1 d v s = showsPrec1 d v s === showsPrec1 d (elems v) s++prop_read_show :: V -> Property+prop_read_show v = read (show v) === v++-- The empty list must not parse.+prop_read_list :: [Int] -> Property+prop_read_list as =+    fmap elems (readMaybe (show as) :: Maybe V) === whenNonEmpty as++prop_readsPrec1 :: [Int] -> Property+prop_readsPrec1 as =+    [ (elems v, rest) | (v, rest) <- readsPrec1 0 (show as) :: [(V, String)] ]+      === [ (as, "") | not (List.null as) ]++-- ---------------------------------------------------------------------- --+-- Functor++prop_fmap :: Fun Int Int -> V -> Property+prop_fmap (Fn f) v = elems (fmap f v) === List.map f (elems v)++prop_functor_identity :: V -> Property+prop_functor_identity v = fmap id v === v++prop_functor_composition :: Fun Int Int -> Fun Int Int -> V -> Property+prop_functor_composition (Fn f) (Fn g) v =+    fmap (f . g) v === (fmap f . fmap g) v++-- ---------------------------------------------------------------------- --+-- Applicative+--+-- '<*>' and '>>=' multiply sizes, so laws chaining several of them run on+-- small inputs.++prop_pure :: Int -> Property+prop_pure a = elems (pure a :: V) === [a]++prop_ap :: NonEmptyVector (Fun Int Int) -> V -> Property+prop_ap fs v =+    elems (fmap applyFun fs <*> v)+      === (List.map applyFun (elems fs) <*> elems v)++prop_liftA2 :: Fun (Int, Int) Int -> V -> V -> Property+prop_liftA2 (Fn2 f) v w =+    elems (Applicative.liftA2 f v w)+      === Applicative.liftA2 f (elems v) (elems w)++prop_then :: V -> V -> Property+prop_then v w = elems (v *> w) === (elems v *> elems w)++prop_before :: V -> V -> Property+prop_before v w = elems (v <* w) === (elems v <* elems w)++prop_applicative_identity :: V -> Property+prop_applicative_identity v = (pure id <*> v) === v++prop_applicative_homomorphism :: Fun Int Int -> Int -> Property+prop_applicative_homomorphism (Fn f) a = (pure f <*> pure a) === (pure (f a) :: V)++prop_applicative_interchange :: NonEmptyVector (Fun Int Int) -> Int -> Property+prop_applicative_interchange fs a = (u <*> pure a) === (pure ($ a) <*> u)+  where+    u = fmap applyFun fs++prop_applicative_composition :: Property+prop_applicative_composition = forAll (resize 10 arbitrary) $ \(fs, gs, w) ->+    let u = fmap applyFun (fs :: NonEmptyVector (Fun Int Int))+        v = fmap applyFun (gs :: NonEmptyVector (Fun Int Int))+    in (pure (.) <*> u <*> v <*> (w :: V)) === (u <*> (v <*> w))++-- ---------------------------------------------------------------------- --+-- Monad++prop_bind :: V -> Fun Int V -> Property+prop_bind v (Fn f) = elems (v >>= f) === (elems v >>= elems . f)++prop_monad_left_identity :: Int -> Fun Int V -> Property+prop_monad_left_identity a (Fn f) = (pure a >>= f) === f a++prop_monad_right_identity :: V -> Property+prop_monad_right_identity v = (v >>= pure) === v++prop_monad_associativity :: Property+prop_monad_associativity = forAll (resize 10 arbitrary) $ \(v, Fn f, Fn g) ->+    ((v :: V) >>= f >>= g) === (v >>= (\a -> f a >>= (g :: Int -> V)))++-- ---------------------------------------------------------------------- --+-- MonadZip++prop_mzip :: V -> V -> Property+prop_mzip v w = elems (mzip v w) === List.zip (elems v) (elems w)++prop_mzipWith :: Fun (Int, Int) Int -> V -> V -> Property+prop_mzipWith (Fn2 f) v w =+    elems (mzipWith f v w) === List.zipWith f (elems v) (elems w)++prop_munzip :: NonEmptyVector (Int, Int) -> Property+prop_munzip v = (elems a, elems b) === List.unzip (elems v)+  where+    (a, b) = munzip v++-- Zipping vectors of equal shape loses no information.+prop_monadzip_information :: Fun Int Int -> V -> Property+prop_monadzip_information (Fn f) v = munzip (mzip v w) === (v, w)+  where+    w = fmap f v++-- ---------------------------------------------------------------------- --+-- Semigroup++prop_append :: V -> V -> Property+prop_append v w = elems (v <> w) === elems v ++ elems w++prop_semigroup_associativity :: V -> V -> V -> Property+prop_semigroup_associativity u v w = ((u <> v) <> w) === (u <> (v <> w))++prop_sconcat :: V -> [V] -> Property+prop_sconcat v vs = elems (sconcat (v :| vs)) === List.concatMap elems (v : vs)++prop_stimes :: V -> Property+prop_stimes v = forAll (choose (1, 5)) $ \n ->+    elems (stimes (n :: Int) v) === List.concat (List.replicate n (elems v))++-- A non-positive multiplier would produce an empty vector, so it must throw.+prop_stimes_non_positive :: V -> Property+prop_stimes_non_positive v = forAll (choose (-5, 0)) $ \n ->+    throws (stimes (n :: Int) v)++-- ---------------------------------------------------------------------- --+-- Foldable+--+-- Order-sensitive monoids and fold functions pin down traversal order.++prop_foldable_toList :: V -> Property+prop_foldable_toList v = Foldable.toList v === elems v++prop_foldable_foldMap :: Fun Int [Int] -> V -> Property+prop_foldable_foldMap (Fn f) v = Foldable.foldMap f v === Foldable.foldMap f (elems v)++prop_foldable_foldr :: Fun (Int, Int) Int -> Int -> V -> Property+prop_foldable_foldr (Fn2 f) z v =+    Foldable.foldr f z v === Foldable.foldr f z (elems v)++prop_foldable_foldl :: Fun (Int, Int) Int -> Int -> V -> Property+prop_foldable_foldl (Fn2 f) z v =+    Foldable.foldl f z v === Foldable.foldl f z (elems v)++prop_foldable_length :: V -> Property+prop_foldable_length v = Foldable.length v === List.length (elems v)++prop_foldable_null :: V -> Property+prop_foldable_null v = Foldable.null v === False++prop_foldable_elem :: Int -> V -> Property+prop_foldable_elem a v =+    Foldable.elem (a `mod` 4) w === Foldable.elem (a `mod` 4) (elems w)+  where+    w = vec (List.map (`mod` 4) (elems v))++prop_foldable_sum_product :: V -> Property+prop_foldable_sum_product v =+    (Foldable.sum v, Foldable.product v)+      === (Foldable.sum (elems v), Foldable.product (elems v))++prop_foldable_maximum_minimum :: V -> Property+prop_foldable_maximum_minimum v =+    (Foldable.maximum v, Foldable.minimum v)+      === (Foldable.maximum (elems1 v), Foldable.minimum (elems1 v))++-- ---------------------------------------------------------------------- --+-- Foldable1+--+-- The instance for non-empty lists serves as the model.++#if MIN_VERSION_base(4,18,0)+prop_foldable1_foldMap1 :: Fun Int [Int] -> V -> Property+prop_foldable1_foldMap1 (Fn f) v =+    Foldable1.foldMap1 f v === Foldable1.foldMap1 f (elems1 v)++prop_foldable1_foldMap1' :: Fun Int [Int] -> V -> Property+prop_foldable1_foldMap1' (Fn f) v =+    Foldable1.foldMap1' f v === Foldable1.foldMap1' f (elems1 v)++prop_foldable1_fold1 :: NonEmptyVector [Int] -> Property+prop_foldable1_fold1 v = Foldable1.fold1 v === Foldable1.fold1 (elems1 v)++prop_foldable1_toNonEmpty :: V -> Property+prop_foldable1_toNonEmpty v = Foldable1.toNonEmpty v === elems1 v++prop_foldable1_head_last :: V -> Property+prop_foldable1_head_last v =+    (Foldable1.head v, Foldable1.last v)+      === (Foldable1.head (elems1 v), Foldable1.last (elems1 v))++prop_foldable1_maximum_minimum :: V -> Property+prop_foldable1_maximum_minimum v =+    (Foldable1.maximum v, Foldable1.minimum v)+      === (Foldable1.maximum (elems1 v), Foldable1.minimum (elems1 v))++prop_foldable1_foldrMap1 :: Fun Int Int -> Fun (Int, Int) Int -> V -> Property+prop_foldable1_foldrMap1 (Fn f) (Fn2 g) v =+    (Foldable1.foldrMap1 f g v, Foldable1.foldrMap1' f g v)+      === ( Foldable1.foldrMap1 f g (elems1 v)+          , Foldable1.foldrMap1' f g (elems1 v)+          )++prop_foldable1_foldlMap1 :: Fun Int Int -> Fun (Int, Int) Int -> V -> Property+prop_foldable1_foldlMap1 (Fn f) (Fn2 g) v =+    (Foldable1.foldlMap1 f g v, Foldable1.foldlMap1' f g v)+      === ( Foldable1.foldlMap1 f g (elems1 v)+          , Foldable1.foldlMap1' f g (elems1 v)+          )+#endif++-- ---------------------------------------------------------------------- --+-- Traversable++prop_traverse :: Fun Int Int -> V -> Property+prop_traverse (Fn f) v =+    fmap elems (traverse (\a -> logged a (f a)) v)+      === traverse (\a -> logged a (f a)) (elems v)++prop_sequenceA :: NonEmptyVector ([Int], Int) -> Property+prop_sequenceA v = fmap elems (sequenceA v) === sequenceA (elems v)++prop_traversable_identity :: V -> Property+prop_traversable_identity v = traverse Identity v === Identity v++prop_traversable_composition+    :: Fun Int (Maybe Int)+    -> Fun Int ([Int], Int)+    -> V+    -> Property+prop_traversable_composition (Fn f) (Fn g) v =+    traverse (Compose . fmap g . f) v+      === (Compose . fmap (traverse g) . traverse f) v++-- ---------------------------------------------------------------------- --+-- NFData++prop_rnf :: V -> Property+prop_rnf v = rnf v === ()++prop_rnf_forces_elements :: Property+prop_rnf_forces_elements = once $+    throwsIO (evaluate (rnf (NEV.singleton (error "unforced element" :: Int))))++-- ---------------------------------------------------------------------- --+-- Data++-- The representation is a single constructor wrapping a single field.+prop_data :: V -> Property+prop_data v =+    gmapT id v === v+      .&&. gmapM Just v === Just v+      .&&. gmapQ (const ()) v === [()]+      .&&. showConstr (toConstr v) === "NonEmptyVector"
+ test/Test/Data/Vector/NonEmpty/Modification.hs view
@@ -0,0 +1,184 @@+-- |+-- Module      : Test.Data.Vector.NonEmpty.Modification+-- Copyright   : (c) 2019-2026 Emily Pillmore+-- License     : BSD-style+--+-- Maintainer  : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability   : experimental+-- Portability : non-portable+--+-- Properties for bulk updates, accumulations, permutations, and safe+-- destructive updates.+--+module Test.Data.Vector.NonEmpty.Modification+( tests+) where+++import qualified Data.List as List+import Data.Maybe (mapMaybe)+import qualified Data.Vector as V+import qualified Data.Vector.Mutable as MV+import Data.Vector.NonEmpty (NonEmptyVector)+import qualified Data.Vector.NonEmpty as NEV++import Test.Data.Vector.NonEmpty.Util+import Test.QuickCheck+import Test.Tasty+import Test.Tasty.QuickCheck+++tests :: TestTree+tests = testGroup "Modification"+    [ testGroup "Bulk updates"+      [ testProperty "prop_bulk_update" prop_bulk_update+      , testProperty "prop_bulk_update_out_of_bounds" prop_bulk_update_out_of_bounds+      , testProperty "prop_update" prop_update+      , testProperty "prop_update_" prop_update_+      , testProperty "prop_unsafeUpd" prop_unsafeUpd+      , testProperty "prop_unsafeUpdate" prop_unsafeUpdate+      , testProperty "prop_unsafeUpdate_" prop_unsafeUpdate_+      ]+    , testGroup "Accumulations"+      [ testProperty "prop_accum" prop_accum+      , testProperty "prop_accum_out_of_bounds" prop_accum_out_of_bounds+      , testProperty "prop_accumulate" prop_accumulate+      , testProperty "prop_accumulate_" prop_accumulate_+      , testProperty "prop_unsafeAccum" prop_unsafeAccum+      , testProperty "prop_unsafeAccumulate" prop_unsafeAccumulate+      , testProperty "prop_unsafeAccumulate_" prop_unsafeAccumulate_+      ]+    , testGroup "Permutations"+      [ testProperty "prop_reverse" prop_reverse+      , testProperty "prop_reverse_involutive" prop_reverse_involutive+      , testProperty "prop_backpermute" prop_backpermute+      , testProperty "prop_backpermute_out_of_bounds" prop_backpermute_out_of_bounds+      , testProperty "prop_unsafeBackpermute" prop_unsafeBackpermute+      ]+    , testGroup "Safe destructive updates"+      [ testProperty "prop_modify" prop_modify+      , testProperty "prop_modify_preserves_input" prop_modify_preserves_input+      ]+    ]++-- ---------------------------------------------------------------------- --+-- Models++-- | Later updates to the same index win.+--+updateList :: [(Int, a)] -> [a] -> [a]+updateList us as = List.foldl' (\acc (i, a) -> adjustAt i (const a) acc) as us++accumList :: (a -> b -> a) -> [(Int, b)] -> [a] -> [a]+accumList f us as = List.foldl' (\acc (i, b) -> adjustAt i (`f` b) acc) as us++-- ---------------------------------------------------------------------- --+-- Bulk updates++prop_bulk_update :: NonEmptyVector Int -> Property+prop_bulk_update v = forAll (genUpdates v) $ \us ->+    elems (v NEV.// us) === updateList us (elems v)++prop_bulk_update_out_of_bounds :: NonEmptyVector Int -> Int -> Property+prop_bulk_update_out_of_bounds v a = forAll (genOutOfBounds v) $ \i ->+    throws (v NEV.// [(i, a)])++prop_update :: NonEmptyVector Int -> Property+prop_update v = forAll (genUpdates v) $ \us ->+    elems (NEV.update v (V.fromList us)) === updateList us (elems v)++-- Surplus values beyond the index vector are ignored.+prop_update_ :: NonEmptyVector Int -> [Int] -> Property+prop_update_ v surplus = forAll (genUpdates v) $ \us ->+    let (is, as) = List.unzip us+    in elems (NEV.update_ v (V.fromList is) (V.fromList (as ++ surplus)))+         === updateList us (elems v)++prop_unsafeUpd :: NonEmptyVector Int -> Property+prop_unsafeUpd v = forAll (genUpdates v) $ \us ->+    elems (NEV.unsafeUpd v us) === updateList us (elems v)++prop_unsafeUpdate :: NonEmptyVector Int -> Property+prop_unsafeUpdate v = forAll (genUpdates v) $ \us ->+    elems (NEV.unsafeUpdate v (V.fromList us)) === updateList us (elems v)++prop_unsafeUpdate_ :: NonEmptyVector Int -> Property+prop_unsafeUpdate_ v = forAll (genUpdates v) $ \us ->+    let (is, as) = List.unzip us+    in elems (NEV.unsafeUpdate_ v (V.fromList is) (V.fromList as))+         === updateList us (elems v)++-- ---------------------------------------------------------------------- --+-- Accumulations++prop_accum :: Fun (Int, Int) Int -> NonEmptyVector Int -> Property+prop_accum (Fn2 f) v = forAll (genUpdates v) $ \us ->+    elems (NEV.accum f v us) === accumList f us (elems v)++prop_accum_out_of_bounds :: NonEmptyVector Int -> Int -> Property+prop_accum_out_of_bounds v b = forAll (genOutOfBounds v) $ \i ->+    throws (NEV.accum (+) v [(i, b)])++prop_accumulate :: Fun (Int, Int) Int -> NonEmptyVector Int -> Property+prop_accumulate (Fn2 f) v = forAll (genUpdates v) $ \us ->+    elems (NEV.accumulate f v (V.fromList us)) === accumList f us (elems v)++prop_accumulate_ :: Fun (Int, Int) Int -> NonEmptyVector Int -> Property+prop_accumulate_ (Fn2 f) v = forAll (genUpdates v) $ \us ->+    let (is, bs) = List.unzip us+    in elems (NEV.accumulate_ f v (V.fromList is) (V.fromList bs))+         === accumList f us (elems v)++prop_unsafeAccum :: Fun (Int, Int) Int -> NonEmptyVector Int -> Property+prop_unsafeAccum (Fn2 f) v = forAll (genUpdates v) $ \us ->+    elems (NEV.unsafeAccum f v us) === accumList f us (elems v)++prop_unsafeAccumulate :: Fun (Int, Int) Int -> NonEmptyVector Int -> Property+prop_unsafeAccumulate (Fn2 f) v = forAll (genUpdates v) $ \us ->+    elems (NEV.unsafeAccumulate f v (V.fromList us))+      === accumList f us (elems v)++prop_unsafeAccumulate_ :: Fun (Int, Int) Int -> NonEmptyVector Int -> Property+prop_unsafeAccumulate_ (Fn2 f) v = forAll (genUpdates v) $ \us ->+    let (is, bs) = List.unzip us+    in elems (NEV.unsafeAccumulate_ f v (V.fromList is) (V.fromList bs))+         === accumList f us (elems v)++-- ---------------------------------------------------------------------- --+-- Permutations++prop_reverse :: NonEmptyVector Int -> Property+prop_reverse v = elems (NEV.reverse v) === List.reverse (elems v)++prop_reverse_involutive :: NonEmptyVector Int -> Property+prop_reverse_involutive v = NEV.reverse (NEV.reverse v) === v++prop_backpermute :: NonEmptyVector Int -> Property+prop_backpermute v = forAll (genIndices v) $ \is ->+    elems (NEV.backpermute v is)+      === mapMaybe (`lookupList` elems v) (elems is)++prop_backpermute_out_of_bounds :: NonEmptyVector Int -> Property+prop_backpermute_out_of_bounds v = forAll (genOutOfBounds v) $ \i ->+    throws (NEV.backpermute v (NEV.singleton i))++prop_unsafeBackpermute :: NonEmptyVector Int -> Property+prop_unsafeBackpermute v = forAll (genIndices v) $ \is ->+    elems (NEV.unsafeBackpermute v is)+      === mapMaybe (`lookupList` elems v) (elems is)++-- ---------------------------------------------------------------------- --+-- Safe destructive updates++prop_modify :: NonEmptyVector Int -> Int -> Property+prop_modify v a = forAll ((,) <$> genIndex v <*> genIndex v) $ \(i, j) ->+    elems (NEV.modify (\mv -> MV.write mv i a >> MV.swap mv i j) v)+      === swapAt i j (adjustAt i (const a) (elems v))++-- The input vector is copied, never mutated in place.+prop_modify_preserves_input :: NonEmptyList Int -> Int -> Property+prop_modify_preserves_input (NonEmpty as) a =+    (elems w, elems v) === (List.map (const a) as, as)+  where+    v = vec as+    w = NEV.modify (`MV.set` a) v
+ test/Test/Data/Vector/NonEmpty/Mutable.hs view
@@ -0,0 +1,457 @@+{-# LANGUAGE RankNTypes #-}+-- |+-- Module      : Test.Data.Vector.NonEmpty.Mutable+-- Copyright   : (c) 2019-2026 Emily Pillmore+-- License     : BSD-style+--+-- Maintainer  : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability   : experimental+-- Portability : non-portable+--+-- Properties for non-empty mutable vectors, observed by freezing them+-- into lists.+--+module Test.Data.Vector.NonEmpty.Mutable+( tests+) where+++import Control.Exception (SomeException, evaluate, try)+import Control.Monad.ST (ST, runST)++import Data.Either (isLeft)+import qualified Data.List as List+import qualified Data.List.NonEmpty as NonEmpty+import Data.STRef (modifySTRef', newSTRef, readSTRef)+import qualified Data.Vector as V+import Data.Vector.Mutable (MVector)+import qualified Data.Vector.Mutable as MV+import Data.Vector.NonEmpty.Internal+    ( NonEmptyMVector(..)+    , NonEmptyVector(..)+    , NonEmptyIOVector+    , NonEmptySTVector+    )+import qualified Data.Vector.NonEmpty.Mutable as NEMV++import Test.Data.Vector.NonEmpty.Util+import Test.QuickCheck+import Test.Tasty+import Test.Tasty.QuickCheck+++tests :: TestTree+tests = testGroup "Mutable"+    [ testGroup "Length information"+      [ testProperty "prop_length" prop_length+      ]+    , testGroup "Extracting subvectors"+      [ testProperty "prop_slice" prop_slice+      , testProperty "prop_slice_shares" prop_slice_shares+      , testProperty "prop_init" prop_init+      , testProperty "prop_tail" prop_tail+      , testProperty "prop_take" prop_take+      , testProperty "prop_drop" prop_drop+      , testProperty "prop_splitAt" prop_splitAt+      , testProperty "prop_unsafeSlice" prop_unsafeSlice+      , testProperty "prop_unsafeTake" prop_unsafeTake+      , testProperty "prop_unsafeDrop" prop_unsafeDrop+      ]+    , testGroup "Overlapping"+      [ testProperty "prop_overlaps_self" prop_overlaps_self+      , testProperty "prop_overlaps_distinct" prop_overlaps_distinct+      , testProperty "prop_overlaps_slices" prop_overlaps_slices+      ]+    , testGroup "Conversions"+      [ testProperty "prop_fromMVector" prop_fromMVector+      , testProperty "prop_toMVector" prop_toMVector+      , testProperty "prop_unsafeFromMVector" prop_unsafeFromMVector+      ]+    , testGroup "Initialisation"+      [ testProperty "prop_new" prop_new+      , testProperty "prop_new1" prop_new1+      , testProperty "prop_unsafeNew" prop_unsafeNew+      , testProperty "prop_replicate" prop_replicate+      , testProperty "prop_replicate1" prop_replicate1+      , testProperty "prop_replicateM" prop_replicateM+      , testProperty "prop_replicate1M" prop_replicate1M+      , testProperty "prop_clone" prop_clone+      ]+    , testGroup "Growing"+      [ testProperty "prop_grow" prop_grow+      , testProperty "prop_grow_negative" prop_grow_negative+      , testProperty "prop_unsafeGrow" prop_unsafeGrow+      ]+    , testGroup "Restricting memory usage"+      [ testProperty "prop_clear" prop_clear+      ]+    , testGroup "Accessing individual elements"+      [ testProperty "prop_read" prop_read+      , testProperty "prop_read_out_of_bounds" prop_read_out_of_bounds+      , testProperty "prop_write" prop_write+      , testProperty "prop_write_out_of_bounds" prop_write_out_of_bounds+      , testProperty "prop_modify" prop_modify+      , testProperty "prop_swap" prop_swap+      , testProperty "prop_unsafeRead" prop_unsafeRead+      , testProperty "prop_unsafeWrite" prop_unsafeWrite+      , testProperty "prop_unsafeModify" prop_unsafeModify+      , testProperty "prop_unsafeSwap" prop_unsafeSwap+      ]+    , testGroup "Modifying vectors"+      [ testProperty "prop_nextPermutation" prop_nextPermutation+      , testProperty "prop_nextPermutation_last" prop_nextPermutation_last+      ]+    , testGroup "Filling and copying"+      [ testProperty "prop_set" prop_set+      , testProperty "prop_copy" prop_copy+      , testProperty "prop_copy_length_mismatch" prop_copy_length_mismatch+      , testProperty "prop_unsafeCopy" prop_unsafeCopy+      , testProperty "prop_move" prop_move+      , testProperty "prop_unsafeMove" prop_unsafeMove+      ]+    ]++type V = NonEmptyVector Int++-- ---------------------------------------------------------------------- --+-- Running mutable vectors++thaw :: NonEmptyVector a -> ST s (NonEmptySTVector s a)+thaw = fmap NonEmptyMVector . V.thaw . _neVec++thawIO :: NonEmptyVector a -> IO (NonEmptyIOVector a)+thawIO = fmap NonEmptyMVector . V.thaw . _neVec++-- | Run a computation against a fresh mutable copy of the vector.+--+withThawed :: NonEmptyVector a -> (forall s. NonEmptySTVector s a -> ST s b) -> b+withThawed v k = runST (thaw v >>= k)++freezeList :: MVector s a -> ST s [a]+freezeList = fmap V.toList . V.freeze++freeze :: NonEmptySTVector s a -> ST s [a]+freeze = freezeList . _nemVec++-- | A mutable operation's effect on the vector's contents.+--+mutated :: V -> (forall s. NonEmptySTVector s Int -> ST s ()) -> [Int]+mutated v k = withThawed v (\mv -> k mv >> freeze mv)++-- ---------------------------------------------------------------------- --+-- Length information++prop_length :: V -> Property+prop_length v = withThawed v (pure . NEMV.length) === List.length (elems v)++-- ---------------------------------------------------------------------- --+-- Extracting subvectors++prop_slice :: V -> Property+prop_slice v = forAll (genSlice v) $ \(i, m) ->+    withThawed v (freezeList . NEMV.slice i m)+      === List.take m (List.drop i (elems v))++-- Subvectors are views: writes through them reach the parent vector.+prop_slice_shares :: V -> Int -> Property+prop_slice_shares v a = forAll (genIndex v) $ \i ->+    mutated v (\mv -> MV.write (NEMV.drop i mv) 0 a)+      === adjustAt i (const a) (elems v)++prop_init :: V -> Property+prop_init v =+    withThawed v (freezeList . NEMV.init)+      === List.take (List.length (elems v) - 1) (elems v)++prop_tail :: V -> Property+prop_tail v = withThawed v (freezeList . NEMV.tail) === List.drop 1 (elems v)++prop_take :: Int -> V -> Property+prop_take n v =+    withThawed v (freezeList . NEMV.take n) === List.take n (elems v)++prop_drop :: Int -> V -> Property+prop_drop n v =+    withThawed v (freezeList . NEMV.drop n) === List.drop n (elems v)++prop_splitAt :: Int -> V -> Property+prop_splitAt n v =+    withThawed v (\mv -> let (as, bs) = NEMV.splitAt n mv+                         in (,) <$> freezeList as <*> freezeList bs)+      === List.splitAt n (elems v)++prop_unsafeSlice :: V -> Property+prop_unsafeSlice v = forAll (genSlice v) $ \(i, m) ->+    withThawed v (freezeList . NEMV.unsafeSlice i m)+      === List.take m (List.drop i (elems v))++prop_unsafeTake :: V -> Property+prop_unsafeTake v = forAll (choose (0, List.length (elems v))) $ \n ->+    withThawed v (freezeList . NEMV.unsafeTake n) === List.take n (elems v)++prop_unsafeDrop :: V -> Property+prop_unsafeDrop v = forAll (choose (0, List.length (elems v))) $ \n ->+    withThawed v (freezeList . NEMV.unsafeDrop n) === List.drop n (elems v)++-- ---------------------------------------------------------------------- --+-- Overlapping++prop_overlaps_self :: V -> Property+prop_overlaps_self v = withThawed v (\mv -> pure (NEMV.overlaps mv mv)) === True++prop_overlaps_distinct :: V -> V -> Property+prop_overlaps_distinct v w = runST (NEMV.overlaps <$> thaw v <*> thaw w) === False++-- Adjacent slices are disjoint; extending one by an element overlaps them.+prop_overlaps_slices :: V -> Property+prop_overlaps_slices v = n >= 2 ==> forAll (choose (1, n - 1)) $ \k ->+    withThawed v (\(NonEmptyMVector mv) ->+      let front m = NonEmptyMVector (MV.take m mv)+          back = NonEmptyMVector (MV.drop k mv)+      in pure (NEMV.overlaps (front k) back, NEMV.overlaps (front (k + 1)) back))+      === (False, True)+  where+    n = List.length (elems v)++-- ---------------------------------------------------------------------- --+-- Conversions++prop_fromMVector :: [Int] -> Property+prop_fromMVector as =+    runST (V.thaw (V.fromList as) >>= traverse freeze . NEMV.fromMVector)+      === whenNonEmpty as++prop_toMVector :: V -> Property+prop_toMVector v = withThawed v (freezeList . NEMV.toMVector) === elems v++prop_unsafeFromMVector :: V -> Property+prop_unsafeFromMVector v =+    runST (V.thaw (_neVec v) >>= freeze . NEMV.unsafeFromMVector) === elems v++-- ---------------------------------------------------------------------- --+-- Initialisation++prop_new :: NonNegative Int -> Property+prop_new (NonNegative n) =+    runST (fmap NEMV.length <$> NEMV.new n)+      === if n > 0 then Just n else Nothing++prop_new1 :: Int -> Property+prop_new1 n = runST (NEMV.length <$> NEMV.new1 n) === max n 1++prop_unsafeNew :: NonNegative Int -> Property+prop_unsafeNew (NonNegative n) =+    runST (fmap NEMV.length <$> NEMV.unsafeNew n)+      === if n > 0 then Just n else Nothing++prop_replicate :: Int -> Int -> Property+prop_replicate n a =+    runST (NEMV.replicate n a >>= traverse freeze)+      === whenNonEmpty (List.replicate n a)++prop_replicate1 :: Int -> Int -> Property+prop_replicate1 n a =+    runST (NEMV.replicate1 n a >>= freeze) === List.replicate (max n 1) a++-- The action runs once per element, in order.+prop_replicateM :: Int -> Property+prop_replicateM n =+    runST (counter >>= NEMV.replicateM n >>= traverse freeze)+      === whenNonEmpty [1 .. n]++prop_replicate1M :: Int -> Property+prop_replicate1M n =+    runST (counter >>= NEMV.replicate1M n >>= traverse freeze)+      === Just [1 .. max n 1]++-- | An action yielding 1, 2, 3, ... on successive runs.+--+counter :: ST s (ST s Int)+counter = do+    ref <- newSTRef 0+    pure (modifySTRef' ref (+ 1) >> readSTRef ref)++-- A clone has the same contents but no longer aliases the original.+prop_clone :: V -> Int -> Property+prop_clone v a =+    withThawed v (\mv -> do+      c <- NEMV.clone mv+      MV.set (_nemVec c) a+      (,) <$> freeze mv <*> freeze c)+      === (elems v, List.map (const a) (elems v))++-- ---------------------------------------------------------------------- --+-- Growing++prop_grow :: V -> NonNegative Int -> Property+prop_grow v (NonNegative n) =+    withThawed v (\mv -> do+      g <- NEMV.grow mv n+      as <- freezeList (MV.take (NEMV.length mv) (_nemVec g))+      pure (NEMV.length g, as))+      === (List.length (elems v) + n, elems v)++prop_grow_negative :: V -> Positive Int -> Property+prop_grow_negative v (Positive n) =+    throwsIO (thawIO v >>= \mv -> NEMV.grow mv (negate n))++prop_unsafeGrow :: V -> NonNegative Int -> Property+prop_unsafeGrow v (NonNegative n) =+    withThawed v (\mv -> do+      g <- NEMV.unsafeGrow mv n+      as <- freezeList (MV.take (NEMV.length mv) (_nemVec g))+      pure (NEMV.length g, as))+      === (List.length (elems v) + n, elems v)++-- ---------------------------------------------------------------------- --+-- Restricting memory usage++-- Clearing keeps the length but releases every element.+prop_clear :: V -> Property+prop_clear v = forAll (genIndex v) $ \i -> ioProperty $ do+    mv <- thawIO v+    NEMV.clear mv+    a <- NEMV.read mv i+    r <- try (evaluate a)+    pure $ NEMV.length mv === List.length (elems v)+      .&&. counterexample "element survived clear"+             (isLeft (r :: Either SomeException Int))++-- ---------------------------------------------------------------------- --+-- Accessing individual elements++prop_read :: V -> Property+prop_read v = forAll (genIndex v) $ \i ->+    Just (withThawed v (`NEMV.read` i)) === lookupList i (elems v)++prop_read_out_of_bounds :: V -> Property+prop_read_out_of_bounds v = forAll (genOutOfBounds v) $ \i ->+    throwsIO (thawIO v >>= (`NEMV.read` i))++prop_write :: V -> Int -> Property+prop_write v a = forAll (genIndex v) $ \i ->+    mutated v (\mv -> NEMV.write mv i a) === adjustAt i (const a) (elems v)++prop_write_out_of_bounds :: V -> Int -> Property+prop_write_out_of_bounds v a = forAll (genOutOfBounds v) $ \i ->+    throwsIO (thawIO v >>= \mv -> NEMV.write mv i a)++prop_modify :: Fun Int Int -> V -> Property+prop_modify (Fn f) v = forAll (genIndex v) $ \i ->+    mutated v (\mv -> NEMV.modify mv f i) === adjustAt i f (elems v)++prop_swap :: V -> Property+prop_swap v = forAll ((,) <$> genIndex v <*> genIndex v) $ \(i, j) ->+    mutated v (\mv -> NEMV.swap mv i j) === swapAt i j (elems v)++prop_unsafeRead :: V -> Property+prop_unsafeRead v = forAll (genIndex v) $ \i ->+    Just (withThawed v (`NEMV.unsafeRead` i)) === lookupList i (elems v)++prop_unsafeWrite :: V -> Int -> Property+prop_unsafeWrite v a = forAll (genIndex v) $ \i ->+    mutated v (\mv -> NEMV.unsafeWrite mv i a) === adjustAt i (const a) (elems v)++prop_unsafeModify :: Fun Int Int -> V -> Property+prop_unsafeModify (Fn f) v = forAll (genIndex v) $ \i ->+    mutated v (\mv -> NEMV.unsafeModify mv f i) === adjustAt i f (elems v)++prop_unsafeSwap :: V -> Property+prop_unsafeSwap v = forAll ((,) <$> genIndex v <*> genIndex v) $ \(i, j) ->+    mutated v (\mv -> NEMV.unsafeSwap mv i j) === swapAt i j (elems v)++-- ---------------------------------------------------------------------- --+-- Modifying vectors++-- Starting from sorted order, successive permutations enumerate every+-- distinct permutation in lexicographic order.+prop_nextPermutation :: Property+prop_nextPermutation =+    forAll (choose (1, 5)) $ \n ->+    forAll (vectorOf n (choose (0, 3))) $ \as ->+      let sorted = List.sort (as :: [Int])+      in withThawed (vec sorted) enumerate+           === List.map NonEmpty.head+                 (NonEmpty.group (List.sort (List.permutations sorted)))+  where+    enumerate :: NonEmptySTVector s Int -> ST s [[Int]]+    enumerate mv = do+      as <- freeze mv+      more <- NEMV.nextPermutation mv+      if more then (as :) <$> enumerate mv else pure [as]++-- The last permutation has no successor and is left unchanged.+prop_nextPermutation_last :: V -> Property+prop_nextPermutation_last v =+    withThawed (vec descending) (\mv -> (,) <$> NEMV.nextPermutation mv <*> freeze mv)+      === (False, descending)+  where+    descending = List.sortBy (flip compare) (elems v)++-- ---------------------------------------------------------------------- --+-- Filling and copying++prop_set :: V -> Int -> Property+prop_set v a =+    mutated v (`NEMV.set` a) === List.map (const a) (elems v)++-- | Copy @v@ over a vector of the same length, returning the target and+-- source contents afterwards.+--+copied+    :: (forall s. NonEmptySTVector s Int -> NonEmptySTVector s Int -> ST s ())+    -> V+    -> [Int]+    -> ([Int], [Int])+copied cp v bs = runST $ do+    dst <- thaw (vec bs)+    src <- thaw v+    cp dst src+    (,) <$> freeze dst <*> freeze src++prop_copy :: V -> Property+prop_copy v = forAll (vectorOf (List.length (elems v)) arbitrary) $ \bs ->+    copied NEMV.copy v bs === (elems v, elems v)++prop_copy_length_mismatch :: V -> Property+prop_copy_length_mismatch v = throwsIO $ do+    dst <- thawIO (vec (0 : elems v))+    src <- thawIO v+    NEMV.copy dst src++prop_unsafeCopy :: V -> Property+prop_unsafeCopy v = forAll (vectorOf (List.length (elems v)) arbitrary) $ \bs ->+    copied NEMV.unsafeCopy v bs === (elems v, elems v)++-- | Move between the overlapping slices @[0, n - 1)@ and @[1, n)@ of one+-- vector, in both directions.+--+moved+    :: (forall s. NonEmptySTVector s Int -> NonEmptySTVector s Int -> ST s ())+    -> V+    -> ([Int], [Int])+moved mv v = (shift True, shift False)+  where+    n = List.length (elems v)++    shift left = withThawed v $ \(NonEmptyMVector raw) -> do+      let lo = NonEmptyMVector (MV.slice 0 (n - 1) raw)+          hi = NonEmptyMVector (MV.slice 1 (n - 1) raw)+      if left then mv lo hi else mv hi lo+      freezeList raw++prop_move :: V -> Property+prop_move v = List.length as >= 2 ==>+    moved NEMV.move v+      === (List.drop 1 as ++ List.drop (n - 1) as, List.take 1 as ++ List.take (n - 1) as)+  where+    as = elems v+    n = List.length as++prop_unsafeMove :: V -> Property+prop_unsafeMove v = List.length as >= 2 ==>+    moved NEMV.unsafeMove v+      === (List.drop 1 as ++ List.drop (n - 1) as, List.take 1 as ++ List.take (n - 1) as)+  where+    as = elems v+    n = List.length as
+ test/Test/Data/Vector/NonEmpty/Predicates.hs view
@@ -0,0 +1,192 @@+-- |+-- Module      : Test.Data.Vector.NonEmpty.Predicates+-- Copyright   : (c) 2019-2026 Emily Pillmore+-- License     : BSD-style+--+-- Maintainer  : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability   : experimental+-- Portability : non-portable+--+-- Properties for filtering, partitioning, and searching.+--+-- Elements are reduced modulo a small number where duplicates or hits+-- need to be likely.+--+module Test.Data.Vector.NonEmpty.Predicates+( tests+) where+++import Data.Either (partitionEithers)+import qualified Data.List as List+import qualified Data.List.NonEmpty as NonEmpty+import qualified Data.Maybe as Maybe+import qualified Data.Vector as V+import Data.Vector.NonEmpty (NonEmptyVector)+import qualified Data.Vector.NonEmpty as NEV++import Test.Data.Vector.NonEmpty.Util+import Test.QuickCheck+import Test.Tasty+import Test.Tasty.QuickCheck+++tests :: TestTree+tests = testGroup "Predicates"+    [ testGroup "Filtering"+      [ testProperty "prop_uniq" prop_uniq+      , testProperty "prop_mapMaybe" prop_mapMaybe+      , testProperty "prop_imapMaybe" prop_imapMaybe+      , testProperty "prop_filter" prop_filter+      , testProperty "prop_ifilter" prop_ifilter+      , testProperty "prop_filterM" prop_filterM+      , testProperty "prop_ifilterM" prop_ifilterM+      , testProperty "prop_takeWhile" prop_takeWhile+      , testProperty "prop_dropWhile" prop_dropWhile+      ]+    , testGroup "Partitioning"+      [ testProperty "prop_partition" prop_partition+      , testProperty "prop_partitionWith" prop_partitionWith+      , testProperty "prop_unstablePartition" prop_unstablePartition+      , testProperty "prop_span" prop_span+      , testProperty "prop_break" prop_break+      ]+    , testGroup "Searching"+      [ testProperty "prop_elem" prop_elem+      , testProperty "prop_notElem" prop_notElem+      , testProperty "prop_find" prop_find+      , testProperty "prop_findIndex" prop_findIndex+      , testProperty "prop_findIndices" prop_findIndices+      , testProperty "prop_elemIndex" prop_elemIndex+      , testProperty "prop_elemIndices" prop_elemIndices+      ]+    ]++type V = NonEmptyVector Int++-- | Reduce elements to a small range so that duplicates and matches are+-- common.+--+small :: V -> V+small = vec . List.map (`mod` 4) . elems++-- ---------------------------------------------------------------------- --+-- Filtering++prop_uniq :: V -> Property+prop_uniq v =+    elems (NEV.uniq w) === List.map NonEmpty.head (NonEmpty.group (elems w))+  where+    w = small v++prop_mapMaybe :: Fun Int (Maybe Int) -> V -> Property+prop_mapMaybe (Fn f) v =+    V.toList (NEV.mapMaybe f v) === Maybe.mapMaybe f (elems v)++prop_imapMaybe :: Fun (Int, Int) (Maybe Int) -> V -> Property+prop_imapMaybe (Fn2 f) v =+    V.toList (NEV.imapMaybe f v)+      === Maybe.catMaybes (List.zipWith f [0 ..] (elems v))++prop_filter :: Fun Int Bool -> V -> Property+prop_filter (Fn p) v = V.toList (NEV.filter p v) === List.filter p (elems v)++prop_ifilter :: Fun (Int, Int) Bool -> V -> Property+prop_ifilter (Fn2 p) v =+    V.toList (NEV.ifilter p v)+      === [ a | (i, a) <- List.zip [0 ..] (elems v), p i a ]++prop_filterM :: Fun Int Bool -> V -> Property+prop_filterM (Fn p) v =+    fmap V.toList (NEV.filterM (\a -> logged a (p a)) v)+      === (elems v, List.filter p (elems v))++prop_ifilterM :: Fun (Int, Int) Bool -> V -> Property+prop_ifilterM (Fn2 p) v =+    fmap V.toList (NEV.ifilterM (\i a -> logged i (p i a)) v)+      === ( List.map fst ias+          , [ a | (i, a) <- ias, p i a ]+          )+  where+    ias = List.zip [0 ..] (elems v)++prop_takeWhile :: Fun Int Bool -> V -> Property+prop_takeWhile (Fn p) v =+    V.toList (NEV.takeWhile p v) === List.takeWhile p (elems v)++prop_dropWhile :: Fun Int Bool -> V -> Property+prop_dropWhile (Fn p) v =+    V.toList (NEV.dropWhile p v) === List.dropWhile p (elems v)++-- ---------------------------------------------------------------------- --+-- Partitioning++prop_partition :: Fun Int Bool -> V -> Property+prop_partition (Fn p) v =+    (V.toList as, V.toList bs) === List.partition p (elems v)+  where+    (as, bs) = NEV.partition p v++prop_partitionWith :: Fun Int (Either Int Int) -> V -> Property+prop_partitionWith (Fn f) v =+    (V.toList as, V.toList bs) === partitionEithers (List.map f (elems v))+  where+    (as, bs) = NEV.partitionWith f v++-- Only the contents of each side are specified, not their order.+prop_unstablePartition :: Fun Int Bool -> V -> Property+prop_unstablePartition (Fn p) v =+    (List.sort (V.toList as), List.sort (V.toList bs))+      === (List.sort ts, List.sort fs)+  where+    (as, bs) = NEV.unstablePartition p v+    (ts, fs) = List.partition p (elems v)++prop_span :: Fun Int Bool -> V -> Property+prop_span (Fn p) v =+    (V.toList as, V.toList bs) === List.span p (elems v)+  where+    (as, bs) = NEV.span p v++prop_break :: Fun Int Bool -> V -> Property+prop_break (Fn p) v =+    (V.toList as, V.toList bs) === List.break p (elems v)+  where+    (as, bs) = NEV.break p v++-- ---------------------------------------------------------------------- --+-- Searching++prop_elem :: Int -> V -> Property+prop_elem a v = NEV.elem (a `mod` 4) w === List.elem (a `mod` 4) (elems w)+  where+    w = small v++prop_notElem :: Int -> V -> Property+prop_notElem a v =+    NEV.notElem (a `mod` 4) w === List.notElem (a `mod` 4) (elems w)+  where+    w = small v++prop_find :: Fun Int Bool -> V -> Property+prop_find (Fn p) v = NEV.find p v === List.find p (elems v)++prop_findIndex :: Fun Int Bool -> V -> Property+prop_findIndex (Fn p) v = NEV.findIndex p v === List.findIndex p (elems v)++prop_findIndices :: Fun Int Bool -> V -> Property+prop_findIndices (Fn p) v =+    V.toList (NEV.findIndices p v) === List.findIndices p (elems v)++prop_elemIndex :: Int -> V -> Property+prop_elemIndex a v =+    NEV.elemIndex (a `mod` 4) w === List.elemIndex (a `mod` 4) (elems w)+  where+    w = small v++prop_elemIndices :: Int -> V -> Property+prop_elemIndices a v =+    V.toList (NEV.elemIndices (a `mod` 4) w)+      === List.elemIndices (a `mod` 4) (elems w)+  where+    w = small v
+ test/Test/Data/Vector/NonEmpty/Scans.hs view
@@ -0,0 +1,215 @@+-- |+-- Module      : Test.Data.Vector.NonEmpty.Scans+-- Copyright   : (c) 2019-2026 Emily Pillmore+-- License     : BSD-style+--+-- Maintainer  : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability   : experimental+-- Portability : non-portable+--+-- Properties for prefix sums (scans) and transformations.+--+-- Scan functions are arbitrary, hence neither associative nor commutative,+-- so the models pin down scan direction and argument order.+--+module Test.Data.Vector.NonEmpty.Scans+( tests+) where+++import qualified Data.List as List+import qualified Data.Vector as V+import Data.Vector.NonEmpty (NonEmptyVector)+import qualified Data.Vector.NonEmpty as NEV++import Test.Data.Vector.NonEmpty.Util+import Test.QuickCheck+import Test.Tasty+import Test.Tasty.QuickCheck+++tests :: TestTree+tests = testGroup "Scans"+    [ testGroup "Prefix sums"+      [ testProperty "prop_prescanl" prop_prescanl+      , testProperty "prop_prescanl'" prop_prescanl'+      , testProperty "prop_postscanl" prop_postscanl+      , testProperty "prop_postscanl'" prop_postscanl'+      , testProperty "prop_scanl" prop_scanl+      , testProperty "prop_scanl'" prop_scanl'+      , testProperty "prop_scanl1" prop_scanl1+      , testProperty "prop_scanl1'" prop_scanl1'+      , testProperty "prop_iscanl" prop_iscanl+      , testProperty "prop_iscanl'" prop_iscanl'+      , testProperty "prop_prescanr" prop_prescanr+      , testProperty "prop_prescanr'" prop_prescanr'+      , testProperty "prop_postscanr" prop_postscanr+      , testProperty "prop_postscanr'" prop_postscanr'+      , testProperty "prop_scanr" prop_scanr+      , testProperty "prop_scanr'" prop_scanr'+      , testProperty "prop_scanr1" prop_scanr1+      , testProperty "prop_scanr1'" prop_scanr1'+      , testProperty "prop_iscanr" prop_iscanr+      , testProperty "prop_iscanr'" prop_iscanr'+      ]+    , testGroup "Intersperse tests"+      [ testProperty "prop_intersperse" prop_intersperse+      , testProperty "prop_intersperse_length" prop_intersperse_length+      , testProperty "prop_intersperse_first_last" prop_intersperse_first_last+      , testProperty "prop_intersperse_separator" prop_intersperse_separator+      , testProperty "prop_intersperse_reconstruction" prop_intersperse_reconstruction+      , testProperty "prop_intersperse_singleton" prop_intersperse_singleton+      , testProperty "prop_explicit_examples" prop_explicit_examples+      ]+    ]++type V = NonEmptyVector Int++-- ---------------------------------------------------------------------- --+-- Models++-- | Left scan over @(index, element)@ pairs.+--+iscanlList :: (Int -> a -> b -> a) -> a -> [b] -> [a]+iscanlList f z = List.scanl (\acc (i, b) -> f i acc b) z . List.zip [0 ..]++-- | Right scan over @(index, element)@ pairs.+--+iscanrList :: (Int -> a -> b -> b) -> b -> [a] -> [b]+iscanrList f z = List.scanr (\(i, a) acc -> f i a acc) z . List.zip [0 ..]++-- ---------------------------------------------------------------------- --+-- Left scans++prop_prescanl :: Fun (Int, Int) Int -> Int -> V -> Property+prop_prescanl (Fn2 f) z v =+    elems (NEV.prescanl f z v)+      === List.take (NEV.length v) (List.scanl f z (elems v))++prop_prescanl' :: Fun (Int, Int) Int -> Int -> V -> Property+prop_prescanl' (Fn2 f) z v =+    elems (NEV.prescanl' f z v)+      === List.take (NEV.length v) (List.scanl f z (elems v))++prop_postscanl :: Fun (Int, Int) Int -> Int -> V -> Property+prop_postscanl (Fn2 f) z v =+    elems (NEV.postscanl f z v) === List.drop 1 (List.scanl f z (elems v))++prop_postscanl' :: Fun (Int, Int) Int -> Int -> V -> Property+prop_postscanl' (Fn2 f) z v =+    elems (NEV.postscanl' f z v) === List.drop 1 (List.scanl f z (elems v))++prop_scanl :: Fun (Int, Int) Int -> Int -> V -> Property+prop_scanl (Fn2 f) z v = elems (NEV.scanl f z v) === List.scanl f z (elems v)++prop_scanl' :: Fun (Int, Int) Int -> Int -> V -> Property+prop_scanl' (Fn2 f) z v = elems (NEV.scanl' f z v) === List.scanl f z (elems v)++prop_scanl1 :: Fun (Int, Int) Int -> V -> Property+prop_scanl1 (Fn2 f) v = elems (NEV.scanl1 f v) === List.scanl1 f (elems v)++prop_scanl1' :: Fun (Int, Int) Int -> V -> Property+prop_scanl1' (Fn2 f) v = elems (NEV.scanl1' f v) === List.scanl1 f (elems v)++prop_iscanl :: Fun (Int, Int, Int) Int -> Int -> V -> Property+prop_iscanl (Fn3 f) z v =+    elems (NEV.iscanl f z v) === iscanlList f z (elems v)++prop_iscanl' :: Fun (Int, Int, Int) Int -> Int -> V -> Property+prop_iscanl' (Fn3 f) z v =+    elems (NEV.iscanl' f z v) === iscanlList f z (elems v)++-- ---------------------------------------------------------------------- --+-- Right scans++prop_prescanr :: Fun (Int, Int) Int -> Int -> V -> Property+prop_prescanr (Fn2 f) z v =+    elems (NEV.prescanr f z v) === List.drop 1 (List.scanr f z (elems v))++prop_prescanr' :: Fun (Int, Int) Int -> Int -> V -> Property+prop_prescanr' (Fn2 f) z v =+    elems (NEV.prescanr' f z v) === List.drop 1 (List.scanr f z (elems v))++prop_postscanr :: Fun (Int, Int) Int -> Int -> V -> Property+prop_postscanr (Fn2 f) z v =+    elems (NEV.postscanr f z v)+      === List.take (NEV.length v) (List.scanr f z (elems v))++prop_postscanr' :: Fun (Int, Int) Int -> Int -> V -> Property+prop_postscanr' (Fn2 f) z v =+    elems (NEV.postscanr' f z v)+      === List.take (NEV.length v) (List.scanr f z (elems v))++prop_scanr :: Fun (Int, Int) Int -> Int -> V -> Property+prop_scanr (Fn2 f) z v = elems (NEV.scanr f z v) === List.scanr f z (elems v)++prop_scanr' :: Fun (Int, Int) Int -> Int -> V -> Property+prop_scanr' (Fn2 f) z v = elems (NEV.scanr' f z v) === List.scanr f z (elems v)++prop_scanr1 :: Fun (Int, Int) Int -> V -> Property+prop_scanr1 (Fn2 f) v = elems (NEV.scanr1 f v) === List.scanr1 f (elems v)++prop_scanr1' :: Fun (Int, Int) Int -> V -> Property+prop_scanr1' (Fn2 f) v = elems (NEV.scanr1' f v) === List.scanr1 f (elems v)++prop_iscanr :: Fun (Int, Int, Int) Int -> Int -> V -> Property+prop_iscanr (Fn3 f) z v =+    elems (NEV.iscanr f z v) === iscanrList f z (elems v)++prop_iscanr' :: Fun (Int, Int, Int) Int -> Int -> V -> Property+prop_iscanr' (Fn3 f) z v =+    elems (NEV.iscanr' f z v) === iscanrList f z (elems v)++-- ---------------------------------------------------------------------- --+-- Intersperse properties++prop_intersperse :: Int -> V -> Property+prop_intersperse sep v =+    elems (NEV.intersperse sep v) === List.intersperse sep (elems v)++-- For vectors with length > 1, interspersing should result in length 2*n - 1+prop_intersperse_length :: Int -> V -> Bool+prop_intersperse_length sep v =+  let result = NEV.intersperse sep v+      originalLen = NEV.length v+  in if originalLen > 1+     then NEV.length result == 2 * originalLen - 1+     else NEV.length result == originalLen++-- The first and last elements of the result should match the original vector+prop_intersperse_first_last :: Int -> V -> Bool+prop_intersperse_first_last sep v =+  let result = NEV.intersperse sep v+  in NEV.head result == NEV.head v && NEV.last result == NEV.last v++-- Every odd-indexed element should be the separator (for vectors with length > 1)+prop_intersperse_separator :: Int -> V -> Bool+prop_intersperse_separator sep v =+  let result = NEV.intersperse sep v+  in NEV.length v <= 1 ||+     all (\i -> result NEV.! (2*i+1) == sep) [0 .. NEV.length v - 2]++-- If we remove the separator, we should get back the original vector+prop_intersperse_reconstruction :: Int -> V -> Bool+prop_intersperse_reconstruction sep v =+  let result = NEV.intersperse sep v+      resultVector = NEV.toVector result+      reconstructed = V.ifilter (\i _ -> i `mod` 2 == 0) resultVector+  in V.toList reconstructed == NEV.toList v++-- For singleton vectors, interspersing should not change the vector+prop_intersperse_singleton :: Int -> Int -> Bool+prop_intersperse_singleton x sep =+  let singleton = NEV.singleton x+      result = NEV.intersperse sep singleton+  in result == singleton++-- Explicit test cases for edge cases and examples combined into one property+prop_explicit_examples :: Property+prop_explicit_examples =+  let normalCase = NEV.intersperse (0 :: Int) (NEV.unsafeFromList [1,2,3 :: Int])+                   === NEV.unsafeFromList [1,0,2,0,3 :: Int]++      singletonCase = NEV.intersperse (0 :: Int) (NEV.singleton (5 :: Int))+                      === NEV.singleton (5 :: Int)+  in normalCase .&&. singletonCase
+ test/Test/Data/Vector/NonEmpty/Util.hs view
@@ -0,0 +1,201 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wno-orphans #-}+-- |+-- Module      : Test.Data.Vector.NonEmpty.Util+-- Copyright   : (c) 2019-2026 Emily Pillmore+-- License     : BSD-style+--+-- Maintainer  : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability   : experimental+-- Portability : non-portable+--+-- Generators, list models, and property helpers shared by the+-- non-empty vector test suite.+--+-- Vectors are built and observed through the representation exposed by+-- "Data.Vector.NonEmpty.Internal", so that the models never depend on+-- the API under test.+--+module Test.Data.Vector.NonEmpty.Util+( -- * Building and observing vectors+  vec+, elems+, elems1+, whenNonEmpty+  -- * Generators+, genIndex+, genIndices+, genOutOfBounds+, genSlice+, genUpdates+  -- * Effects+, logged+, unfoldStep+  -- * List models+, adjustAt+, lookupList+, swapAt+, unfoldrMList+, unfoldrNMList+  -- * Exceptions+, throws+, throwsIO+) where+++import Control.DeepSeq (NFData, force)+import Control.Exception (SomeException, evaluate, try)++import Data.List.NonEmpty (NonEmpty(..))+import Data.Maybe (fromMaybe, listToMaybe)+import qualified Data.Vector as V+import Data.Vector.NonEmpty.Internal (NonEmptyVector(..))++import Test.QuickCheck+++instance Arbitrary a => Arbitrary (NonEmptyVector a) where+    arbitrary = vec . getNonEmpty <$> arbitrary+    shrink = fmap (vec . getNonEmpty) . shrink . NonEmpty . elems++-- ---------------------------------------------------------------------- --+-- Building and observing vectors++-- | Build a non-empty vector from its representation. The caller+-- guarantees that the list is non-empty.+--+vec :: [a] -> NonEmptyVector a+vec = NonEmptyVector . V.fromList++-- | The elements of a non-empty vector, read from its representation.+--+elems :: NonEmptyVector a -> [a]+elems = V.toList . _neVec++-- | The elements of a non-empty vector as a non-empty list, read from its+-- representation.+--+elems1 :: NonEmptyVector a -> NonEmpty a+elems1 (NonEmptyVector v) = V.unsafeHead v :| V.toList (V.unsafeTail v)++-- | Model of the 'Maybe'-valued constructors: 'Nothing' exactly when the+-- result would be empty.+--+whenNonEmpty :: [a] -> Maybe [a]+whenNonEmpty [] = Nothing+whenNonEmpty as = Just as++-- ---------------------------------------------------------------------- --+-- Generators++-- | A valid index into the vector.+--+genIndex :: NonEmptyVector a -> Gen Int+genIndex (NonEmptyVector v) = choose (0, V.length v - 1)++-- | A non-empty vector of valid indices into the vector.+--+genIndices :: NonEmptyVector a -> Gen (NonEmptyVector Int)+genIndices v = vec <$> listOf1 (genIndex v)++-- | An index outside the bounds of the vector, on either side.+--+genOutOfBounds :: NonEmptyVector a -> Gen Int+genOutOfBounds (NonEmptyVector v) = oneof+    [ choose (-10, -1)+    , choose (n, n + 10)+    ]+  where+    n = V.length v++-- | A valid @(offset, length)@ slice of the vector, possibly empty.+--+genSlice :: NonEmptyVector a -> Gen (Int, Int)+genSlice (NonEmptyVector v) = do+    i <- choose (0, n)+    m <- choose (0, n - i)+    pure (i, m)+  where+    n = V.length v++-- | A list of updates at valid indices of the vector.+--+genUpdates :: Arbitrary b => NonEmptyVector a -> Gen [(Int, b)]+genUpdates v = listOf ((,) <$> genIndex v <*> arbitrary)++-- ---------------------------------------------------------------------- --+-- Effects++-- | A writer-style action that records @w@ and yields @a@. Running monadic+-- operations in @([w], -)@ exposes the order and number of effects.+--+logged :: w -> a -> ([w], a)+logged w a = ([w], a)++-- | A terminating unfold step: counts the seed down to zero, emitting+-- @f seed@ at each step.+--+unfoldStep :: (Int -> a) -> Int -> Maybe (a, Int)+unfoldStep f b+    | b <= 0 = Nothing+    | otherwise = Just (f b, b - 1)++-- ---------------------------------------------------------------------- --+-- List models++-- | Apply a function at an index, leaving the list unchanged when the index+-- is out of bounds.+--+adjustAt :: Int -> (a -> a) -> [a] -> [a]+adjustAt i f as = [ if j == i then f a else a | (j, a) <- zip [0 ..] as ]++-- | Total list indexing.+--+lookupList :: Int -> [a] -> Maybe a+lookupList i as+    | i < 0 = Nothing+    | otherwise = listToMaybe (drop i as)++-- | Exchange the elements at two indices.+--+swapAt :: Int -> Int -> [a] -> [a]+swapAt i j as = [ pick k a | (k, a) <- zip [0 ..] as ]+  where+    pick k a+      | k == i = fromMaybe a (lookupList j as)+      | k == j = fromMaybe a (lookupList i as)+      | otherwise = a++-- | Monadic 'Data.List.unfoldr'.+--+unfoldrMList :: Monad m => (b -> m (Maybe (a, b))) -> b -> m [a]+unfoldrMList f = go+  where+    go b = f b >>= maybe (pure []) (\(a, b') -> (a :) <$> go b')++-- | Monadic 'Data.List.unfoldr' producing at most @n@ elements. The step+-- function is not run once @n@ elements have been produced.+--+unfoldrNMList :: Monad m => Int -> (b -> m (Maybe (a, b))) -> b -> m [a]+unfoldrNMList n0 f = go n0+  where+    go n b+      | n <= 0 = pure []+      | otherwise = f b >>= maybe (pure []) (\(a, b') -> (a :) <$> go (n - 1) b')++-- ---------------------------------------------------------------------- --+-- Exceptions++-- | The value throws when fully evaluated.+--+throws :: NFData a => a -> Property+throws = throwsIO . evaluate . force++-- | The action throws when run.+--+throwsIO :: IO a -> Property+throwsIO act = ioProperty $ do+    r <- try act+    pure $ case r of+      Left (_ :: SomeException) -> property True+      Right _ -> counterexample "expected an exception" (property False)