diff --git a/.github/workflows/haskell.yaml b/.github/workflows/haskell.yaml index 2656d83..9683c00 100644 --- a/.github/workflows/haskell.yaml +++ b/.github/workflows/haskell.yaml @@ -10,12 +10,19 @@ jobs: strategy: fail-fast: false matrix: - ghc: ['8.4', '8.6', '8.8', '8.10', '9.0', '9.2', '9.4', '9.6'] + ghc: ['8.4', '8.6', '8.8', '8.10', '9.0', '9.2', '9.4', '9.6', '9.8'] steps: - - uses: actions/checkout@v3 - - uses: haskell/actions/setup@v2 + - uses: actions/checkout@v4 + - uses: haskell-actions/setup@v2 + id: setup-haskell with: ghc-version: ${{ matrix.ghc }} + - uses: actions/cache@v3 + with: + path: | + ${{ steps.setup-haskell.outputs.cabal-store }} + dist-newstyle + key: ${{ runner.os }}-${{ matrix.ghc }} - name: Build run: cabal build - name: Test diff --git a/CHANGELOG.md b/CHANGELOG.md index a238138..01d77d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 0.2.1.0 - December 2023 + +* Add `findIndexL`, `findIndexR`, `findIndicesL`, `findIndicesR` +* Fix bug in `|>` & `<|` ([#10](https://github.com/konsumlamm/rrb-vector/issues/10)) + # 0.2.0.1 - October 2023 * Support `primitive-0.9` and `deepseq-1.5` diff --git a/rrb-vector-strict.cabal b/rrb-vector-strict.cabal index 614fdb8..212a9db 100644 --- a/rrb-vector-strict.cabal +++ b/rrb-vector-strict.cabal @@ -1,5 +1,5 @@ name: rrb-vector-strict -version: 0.2.0.1 +version: 0.2.1.0 synopsis: Efficient RRB-Vectors description: An RRB-Vector is an efficient sequence data structure. @@ -31,8 +31,9 @@ tested-with: GHC == 8.10.7 GHC == 9.0.2 GHC == 9.2.8 - GHC == 9.4.7 + GHC == 9.4.8 GHC == 9.6.3 + GHC == 9.8.1 source-repository head type: git @@ -61,7 +62,7 @@ test-suite test Strictness type: exitcode-stdio-1.0 ghc-options: -Wall -Wno-orphans -Wno-type-defaults - build-depends: base, deepseq, quickcheck-classes-base, rrb-vector-strict, tasty, tasty-quickcheck + build-depends: base, containers, deepseq, quickcheck-classes-base, rrb-vector, tasty, tasty-quickcheck if impl(ghc >= 8.6) build-depends: nothunks default-language: Haskell2010 diff --git a/src/Data/RRBVector/Strict.hs b/src/Data/RRBVector/Strict.hs index 917f011..8ab9e4c 100644 --- a/src/Data/RRBVector/Strict.hs +++ b/src/Data/RRBVector/Strict.hs @@ -33,6 +33,7 @@ module Data.RRBVector.Strict , adjust, adjust' , take, drop, splitAt , insertAt, deleteAt + , findIndexL, findIndexR, findIndicesL, findIndicesR -- * With Index -- -- | Reexported from [indexed-traversable](https://hackage.haskell.org/package/indexed-traversable). diff --git a/src/Data/RRBVector/Strict/Internal.hs b/src/Data/RRBVector/Strict/Internal.hs index e96267a..977fddd 100644 --- a/src/Data/RRBVector/Strict/Internal.hs +++ b/src/Data/RRBVector/Strict/Internal.hs @@ -24,6 +24,7 @@ module Data.RRBVector.Strict.Internal , adjust, adjust' , take, drop, splitAt , insertAt, deleteAt + , findIndexL, findIndexR, findIndicesL, findIndicesR -- * Transformations , map, map', reverse -- * Zipping and unzipping @@ -651,6 +652,22 @@ insertAt i x v = let (left, right) = splitAt i v in (left |> x) >< right deleteAt :: Int -> Vector a -> Vector a deleteAt i v = let (left, right) = splitAt (i + 1) v in take i left >< right +-- | \(O(n)\). Find the first index from the left that satisfies the predicate. +findIndexL :: (a -> Bool) -> Vector a -> Maybe Int +findIndexL f = ifoldr (\i x acc -> if f x then Just i else acc) Nothing + +-- | \(O(n)\). Find the first index from the right that satisfies the predicate. +findIndexR :: (a -> Bool) -> Vector a -> Maybe Int +findIndexR f = ifoldl (\i acc x -> if f x then Just i else acc) Nothing + +-- | \(O(n)\). Find the indices that satisfy the predicate, starting from the left. +findIndicesL :: (a -> Bool) -> Vector a -> [Int] +findIndicesL f = ifoldr (\i x acc -> if f x then i : acc else acc) [] + +-- | \(O(n)\). Find the indices that satisfy the predicate, starting from the right. +findIndicesR :: (a -> Bool) -> Vector a -> [Int] +findIndicesR f = ifoldl (\i acc x -> if f x then i : acc else acc) [] + -- concatenation -- | \(O(\log \max(n_1, n_2))\). Concatenates two vectors. diff --git a/test/Properties.hs b/test/Properties.hs index 62d188a..c283465 100644 --- a/test/Properties.hs +++ b/test/Properties.hs @@ -1,5 +1,7 @@ {-# LANGUAGE CPP #-} +{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} + module Properties ( properties ) where @@ -12,6 +14,7 @@ import Data.List (uncons) import Data.Proxy (Proxy(..)) import Prelude hiding ((==)) -- use @===@ instead +import qualified Data.Sequence as Seq import qualified Data.RRBVector.Strict as V import Test.QuickCheck.Classes.Base import Test.Tasty @@ -135,6 +138,22 @@ properties = testGroup "properties" , testProperty "satisfies `deleteAt 0 v = drop 1 v`" $ \v -> V.deleteAt 0 v === V.drop 1 v , testProperty "satisfies `deleteAt (length v - 1) v = take (length v - 1) v`" $ \v -> V.deleteAt (length v - 1) v === V.take (length v - 1) v ] + , testGroup "findIndexL" + [ testProperty "finds the first index" $ \v (Fn f) -> V.findIndexL f v === Seq.findIndexL f (Seq.fromList (toList v)) + , testProperty "returns Nothing for the empty vector" $ \(Fn f) -> V.findIndexL f V.empty === Nothing + ] + , testGroup "findIndexR" + [ testProperty "finds the last index" $ \v (Fn f) -> V.findIndexR f v === Seq.findIndexR f (Seq.fromList (toList v)) + , testProperty "returns Nothing for the empty vector" $ \(Fn f) -> V.findIndexR f V.empty === Nothing + ] + , localOption (QuickCheckMaxSize 1000) $ testGroup "findIndicesL" + [ testProperty "finds the indices starting from the left" $ \v (Fn f) -> V.findIndicesL f v === Seq.findIndicesL f (Seq.fromList (toList v)) + , testProperty "returns [] for the empty vector" $ \(Fn f) -> V.findIndicesL f V.empty === [] + ] + , localOption (QuickCheckMaxSize 1000) $ testGroup "findIndicesR" + [ testProperty "finds the indices starting from the right" $ \v (Fn f) -> V.findIndicesR f v === Seq.findIndicesR f (Seq.fromList (toList v)) + , testProperty "returns [] for the empty vector" $ \(Fn f) -> V.findIndicesR f V.empty === [] + ] , testGroup "reverse" [ testProperty "reverses the vector" $ \v -> toList (V.reverse v) === reverse (toList v) ]