From b6288170d1597bab7aa8c19cd3b582b517d0569c Mon Sep 17 00:00:00 2001 From: Ignat Insarov Date: Tue, 6 Feb 2024 21:16:41 +0700 Subject: [PATCH 01/14] Write test cases and stub functions. --- src/Data/Text.hs | 14 ++++++++++++++ src/Data/Text/Lazy.hs | 12 ++++++++++++ tests/Tests/Properties/Substrings.hs | 9 +++++++++ tests/Tests/ShareEmpty.hs | 6 +++++- 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/Data/Text.hs b/src/Data/Text.hs index 00e0e2e3..fec714e7 100644 --- a/src/Data/Text.hs +++ b/src/Data/Text.hs @@ -155,7 +155,9 @@ module Data.Text , group , groupBy , inits + , initsNE , tails + , tailsNE -- ** Breaking into many substrings -- $split @@ -227,6 +229,7 @@ import Control.Monad (foldM) import Control.Monad.ST (ST, runST) import qualified Data.Text.Array as A import qualified Data.List as L hiding (head, tail) +import qualified Data.List.NonEmpty as NonEmptyList import Data.Binary (Binary(get, put)) import Data.Monoid (Monoid(..)) import Data.Semigroup (Semigroup(..)) @@ -1528,12 +1531,23 @@ inits t = empty : case t of | otherwise = let !j = i + iter_ t i in Text arr off j : loop j in loop 0 +-- | /O(n)/ Return all initial segments of the given 'Text', shortest +-- first. +initsNE :: Text -> NonEmptyList.NonEmpty Text +-- see Note [Avoid NonEmpty combinators] +initsNE = P.error "Not implemented." + -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. tails :: Text -> [Text] tails t | null t = [empty] | otherwise = t : tails (unsafeTail t) +-- | /O(n)/ Return all final segments of the given 'Text', longest +-- first. +tailsNE :: Text -> NonEmptyList.NonEmpty Text +tailsNE = P.error "Not implemented." + -- $split -- -- Splitting functions in this library do not perform character-wise diff --git a/src/Data/Text/Lazy.hs b/src/Data/Text/Lazy.hs index 9b977d67..42157b66 100644 --- a/src/Data/Text/Lazy.hs +++ b/src/Data/Text/Lazy.hs @@ -156,7 +156,9 @@ module Data.Text.Lazy , group , groupBy , inits + , initsNE , tails + , tailsNE -- ** Breaking into many substrings -- $split @@ -1432,6 +1434,11 @@ inits = (Empty :) . inits' inits' (Chunk t ts) = L.map (\t' -> Chunk t' Empty) (L.drop 1 (T.inits t)) ++ L.map (Chunk t) (inits' ts) +-- | /O(n)/ Return all initial segments of the given 'Text', +-- shortest first. +initsNE :: Text -> NonEmpty Text +initsNE = P.error "Not implemented." + -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. tails :: Text -> [Text] @@ -1440,6 +1447,11 @@ tails ts@(Chunk t ts') | T.length t == 1 = ts : tails ts' | otherwise = ts : tails (Chunk (T.unsafeTail t) ts') +-- | /O(n)/ Return all final segments of the given 'Text', longest +-- first. +tailsNE :: Text -> NonEmpty Text +tailsNE = P.error "Not implemented." + -- $split -- -- Splitting functions in this library do not perform character-wise diff --git a/tests/Tests/Properties/Substrings.hs b/tests/Tests/Properties/Substrings.hs index ddf74dc1..1c39fabe 100644 --- a/tests/Tests/Properties/Substrings.hs +++ b/tests/Tests/Properties/Substrings.hs @@ -15,6 +15,7 @@ import Test.Tasty (TestTree, testGroup) import Test.Tasty.QuickCheck (testProperty) import Tests.QuickCheckUtils import qualified Data.List as L +import qualified Data.List.NonEmpty as NonEmptyList import qualified Data.Text as T import qualified Data.Text.Internal.Fusion as S import qualified Data.Text.Internal.Fusion.Common as S @@ -156,9 +157,13 @@ t_groupBy (applyFun2 -> p) tl_groupBy (applyFun2 -> p) = L.groupBy p `eqP` (map unpackS . TL.groupBy p) t_inits = L.inits `eqP` (map unpackS . T.inits) +t_initsNE = NonEmptyList.inits `eqP` (fmap unpackS . T.initsNE) tl_inits = L.inits `eqP` (map unpackS . TL.inits) +tl_initsNE = NonEmptyList.inits `eqP` (fmap unpackS . TL.initsNE) t_tails = L.tails `eqP` (map unpackS . T.tails) +t_tailsNE = NonEmptyList.tails `eqP` (fmap unpackS . T.tailsNE) tl_tails = L.tails `eqPSqrt` (map unpackS . TL.tails) +tl_tailsNE = NonEmptyList.tails `eqP` (fmap unpackS . TL.tailsNE) spanML :: Monad m => (b -> m Bool) -> [b] -> m ([b], [b]) spanML p s = go [] s @@ -361,9 +366,13 @@ testSubstrings = testProperty "t_groupBy" t_groupBy, testProperty "tl_groupBy" tl_groupBy, testProperty "t_inits" t_inits, + testProperty "t_initsNE" t_initsNE, testProperty "tl_inits" tl_inits, + testProperty "tl_initsNE" tl_initsNE, testProperty "t_tails" t_tails, + testProperty "t_tailsNE" t_tailsNE, testProperty "tl_tails" tl_tails, + testProperty "tl_tailsNE" tl_tailsNE, testProperty "t_spanM" t_spanM, testProperty "t_spanEndM" t_spanEndM, testProperty "tl_spanM" tl_spanM, diff --git a/tests/Tests/ShareEmpty.hs b/tests/Tests/ShareEmpty.hs index efeea75f..7c4e1345 100644 --- a/tests/Tests/ShareEmpty.hs +++ b/tests/Tests/ShareEmpty.hs @@ -15,6 +15,7 @@ import Test.Tasty (TestTree, testGroup) import GHC.Exts import GHC.Stack import qualified Data.List as L +import qualified Data.List.NonEmpty as NonEmptyList import qualified Data.Text as T @@ -105,10 +106,13 @@ tests = testGroup "empty Text values are shared" assertPtrEqEmpty . snd =<< T.spanEndM (const $ pure False) "123" , testCase "groupBy _ empty = [empty]" $ mapM_ assertPtrEqEmpty $ T.groupBy (==) empty , testCase "inits empty = [empty]" $ mapM_ assertPtrEqEmpty $ T.inits empty + , testCase "initsNE empty = singleton empty" $ mapM_ assertPtrEqEmpty $ T.initsNE empty , testCase "inits _ = [empty, ...]" $ assertPtrEqEmpty $ L.head $ T.inits "123" + , testCase "initsNE _ = empty :| ..." $ assertPtrEqEmpty $ NonEmptyList.head $ T.initsNE "123" , testCase "tails empty = [empty]" $ mapM_ assertPtrEqEmpty $ T.tails empty + , testCase "tailsNE empty = singleton empty" $ mapM_ assertPtrEqEmpty $ T.tailsNE empty , testCase "tails _ = [..., empty]" $ assertPtrEqEmpty $ L.last $ T.tails "123" - , testCase "tails _ = [..., empty]" $ assertPtrEqEmpty $ L.last $ T.tails "123" + , testCase "tailsNE _ = reverse (empty :| …)" $ assertPtrEqEmpty $ NonEmptyList.last $ T.tailsNE "123" , testCase "split _ empty = [empty]" $ mapM_ assertPtrEqEmpty $ T.split (== 'a') "" , testCase "filter (const False) _ = empty" $ assertPtrEqEmpty $ T.filter (const False) "1234" , testCase "zipWith const empty empty = empty" $ assertPtrEqEmpty $ T.zipWith const "" "" From ab9a52cdf55158b5b3e33df9f59f7aecd2ee4644 Mon Sep 17 00:00:00 2001 From: Ignat Insarov Date: Tue, 6 Feb 2024 22:14:31 +0700 Subject: [PATCH 02/14] fixup! Write test cases and stub functions. --- src/Data/Text.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Data/Text.hs b/src/Data/Text.hs index fec714e7..a0d88b2c 100644 --- a/src/Data/Text.hs +++ b/src/Data/Text.hs @@ -1534,7 +1534,6 @@ inits t = empty : case t of -- | /O(n)/ Return all initial segments of the given 'Text', shortest -- first. initsNE :: Text -> NonEmptyList.NonEmpty Text --- see Note [Avoid NonEmpty combinators] initsNE = P.error "Not implemented." -- | /O(n)/ Return all final segments of the given 'Text', longest From 88cbf618407f7f518c6d52d19536aa590453f796 Mon Sep 17 00:00:00 2001 From: Ignat Insarov Date: Tue, 6 Feb 2024 22:14:48 +0700 Subject: [PATCH 03/14] Offer high level definitions. --- src/Data/Text.hs | 5 +++-- src/Data/Text/Lazy.hs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Data/Text.hs b/src/Data/Text.hs index a0d88b2c..608cc31f 100644 --- a/src/Data/Text.hs +++ b/src/Data/Text.hs @@ -230,6 +230,7 @@ import Control.Monad.ST (ST, runST) import qualified Data.Text.Array as A import qualified Data.List as L hiding (head, tail) import qualified Data.List.NonEmpty as NonEmptyList +import Data.Maybe (fromMaybe) import Data.Binary (Binary(get, put)) import Data.Monoid (Monoid(..)) import Data.Semigroup (Semigroup(..)) @@ -1534,7 +1535,7 @@ inits t = empty : case t of -- | /O(n)/ Return all initial segments of the given 'Text', shortest -- first. initsNE :: Text -> NonEmptyList.NonEmpty Text -initsNE = P.error "Not implemented." +initsNE = fromMaybe P.undefined . NonEmptyList.nonEmpty . inits -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. @@ -1545,7 +1546,7 @@ tails t | null t = [empty] -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. tailsNE :: Text -> NonEmptyList.NonEmpty Text -tailsNE = P.error "Not implemented." +tailsNE = fromMaybe P.undefined . NonEmptyList.nonEmpty . tails -- $split -- diff --git a/src/Data/Text/Lazy.hs b/src/Data/Text/Lazy.hs index 42157b66..4784da77 100644 --- a/src/Data/Text/Lazy.hs +++ b/src/Data/Text/Lazy.hs @@ -221,6 +221,7 @@ import Data.Data (Data(gfoldl, toConstr, gunfold, dataTypeOf), constrIndex, import Data.Binary (Binary(get, put)) import Data.List.NonEmpty (NonEmpty(..)) import qualified Data.List.NonEmpty as NE +import Data.Maybe (fromMaybe) import Data.Monoid (Monoid(..)) import Data.Semigroup (Semigroup(..)) import Data.String (IsString(..)) @@ -1437,7 +1438,7 @@ inits = (Empty :) . inits' -- | /O(n)/ Return all initial segments of the given 'Text', -- shortest first. initsNE :: Text -> NonEmpty Text -initsNE = P.error "Not implemented." +initsNE = fromMaybe P.undefined . NE.nonEmpty . inits -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. @@ -1450,7 +1451,7 @@ tails ts@(Chunk t ts') -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. tailsNE :: Text -> NonEmpty Text -tailsNE = P.error "Not implemented." +tailsNE = fromMaybe P.undefined . NE.nonEmpty . tails -- $split -- From b436bb28b427500771660adddf001b7fffcafb0b Mon Sep 17 00:00:00 2001 From: Ignat Insarov Date: Wed, 7 Feb 2024 17:09:54 +0700 Subject: [PATCH 04/14] Offer definitions in primitive terms. --- src/Data/Text.hs | 11 +++++++++-- src/Data/Text/Lazy.hs | 10 ++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Data/Text.hs b/src/Data/Text.hs index 608cc31f..a32bca5e 100644 --- a/src/Data/Text.hs +++ b/src/Data/Text.hs @@ -1535,7 +1535,12 @@ inits t = empty : case t of -- | /O(n)/ Return all initial segments of the given 'Text', shortest -- first. initsNE :: Text -> NonEmptyList.NonEmpty Text -initsNE = fromMaybe P.undefined . NonEmptyList.nonEmpty . inits +initsNE t = empty NonEmptyList.:| case t of + Text arr off len -> + let loop i + | i >= len = [] + | otherwise = let !j = i + iter_ t i in Text arr off j : loop j + in loop 0 -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. @@ -1546,7 +1551,9 @@ tails t | null t = [empty] -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. tailsNE :: Text -> NonEmptyList.NonEmpty Text -tailsNE = fromMaybe P.undefined . NonEmptyList.nonEmpty . tails +tailsNE t + | null t = NonEmptyList.singleton empty + | otherwise = t NonEmptyList.:| tails (unsafeTail t) -- $split -- diff --git a/src/Data/Text/Lazy.hs b/src/Data/Text/Lazy.hs index 4784da77..00964dc6 100644 --- a/src/Data/Text/Lazy.hs +++ b/src/Data/Text/Lazy.hs @@ -1438,7 +1438,10 @@ inits = (Empty :) . inits' -- | /O(n)/ Return all initial segments of the given 'Text', -- shortest first. initsNE :: Text -> NonEmpty Text -initsNE = fromMaybe P.undefined . NE.nonEmpty . inits +initsNE = (Empty NE.:|) . inits' + where inits' Empty = [] + inits' (Chunk t ts) = L.map (\t' -> Chunk t' Empty) (L.drop 1 (T.inits t)) + ++ L.map (Chunk t) (inits' ts) -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. @@ -1451,7 +1454,10 @@ tails ts@(Chunk t ts') -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. tailsNE :: Text -> NonEmpty Text -tailsNE = fromMaybe P.undefined . NE.nonEmpty . tails +tailsNE Empty = Empty :| [] +tailsNE ts@(Chunk t ts') + | T.length t == 1 = ts :| tails ts' + | otherwise = ts :| tails (Chunk (T.unsafeTail t) ts') -- $split -- From 1dd30afd9392dc7e5e5224998b2606369941882d Mon Sep 17 00:00:00 2001 From: Ignat Insarov Date: Wed, 7 Feb 2024 17:13:32 +0700 Subject: [PATCH 05/14] fixup! Offer definitions in primitive terms. --- src/Data/Text.hs | 1 - src/Data/Text/Lazy.hs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Data/Text.hs b/src/Data/Text.hs index a32bca5e..1a1bc87c 100644 --- a/src/Data/Text.hs +++ b/src/Data/Text.hs @@ -230,7 +230,6 @@ import Control.Monad.ST (ST, runST) import qualified Data.Text.Array as A import qualified Data.List as L hiding (head, tail) import qualified Data.List.NonEmpty as NonEmptyList -import Data.Maybe (fromMaybe) import Data.Binary (Binary(get, put)) import Data.Monoid (Monoid(..)) import Data.Semigroup (Semigroup(..)) diff --git a/src/Data/Text/Lazy.hs b/src/Data/Text/Lazy.hs index 00964dc6..f9923204 100644 --- a/src/Data/Text/Lazy.hs +++ b/src/Data/Text/Lazy.hs @@ -221,7 +221,6 @@ import Data.Data (Data(gfoldl, toConstr, gunfold, dataTypeOf), constrIndex, import Data.Binary (Binary(get, put)) import Data.List.NonEmpty (NonEmpty(..)) import qualified Data.List.NonEmpty as NE -import Data.Maybe (fromMaybe) import Data.Monoid (Monoid(..)) import Data.Semigroup (Semigroup(..)) import Data.String (IsString(..)) From 730910a35550aff0d4efdf18cd445bba57f8463f Mon Sep 17 00:00:00 2001 From: Ignat Insarov Date: Wed, 7 Feb 2024 18:12:49 +0700 Subject: [PATCH 06/14] Define `inits` in terms of `initsNE`. --- src/Data/Text.hs | 6 +----- src/Data/Text/Lazy.hs | 5 +---- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Data/Text.hs b/src/Data/Text.hs index 1a1bc87c..2ff8c9c5 100644 --- a/src/Data/Text.hs +++ b/src/Data/Text.hs @@ -1525,11 +1525,7 @@ group = groupBy (==) -- | /O(n)/ Return all initial segments of the given 'Text', shortest -- first. inits :: Text -> [Text] -inits t = empty : case t of - Text arr off len -> - let loop i | i >= len = [] - | otherwise = let !j = i + iter_ t i in Text arr off j : loop j - in loop 0 +inits = NonEmptyList.toList . initsNE -- | /O(n)/ Return all initial segments of the given 'Text', shortest -- first. diff --git a/src/Data/Text/Lazy.hs b/src/Data/Text/Lazy.hs index f9923204..cd80a003 100644 --- a/src/Data/Text/Lazy.hs +++ b/src/Data/Text/Lazy.hs @@ -1429,10 +1429,7 @@ groupBy eq (Chunk t ts) = cons x ys : groupBy eq zs -- | /O(n)/ Return all initial segments of the given 'Text', -- shortest first. inits :: Text -> [Text] -inits = (Empty :) . inits' - where inits' Empty = [] - inits' (Chunk t ts) = L.map (\t' -> Chunk t' Empty) (L.drop 1 (T.inits t)) - ++ L.map (Chunk t) (inits' ts) +inits = NE.toList . initsNE -- | /O(n)/ Return all initial segments of the given 'Text', -- shortest first. From e11c08d990d52feb246978d2203ac43fa16313bf Mon Sep 17 00:00:00 2001 From: Ignat Insarov Date: Wed, 7 Feb 2024 18:13:15 +0700 Subject: [PATCH 07/14] Define `tails` in terms of `tailsNE`. --- src/Data/Text.hs | 3 +-- src/Data/Text/Lazy.hs | 5 +---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Data/Text.hs b/src/Data/Text.hs index 2ff8c9c5..88cfa7e1 100644 --- a/src/Data/Text.hs +++ b/src/Data/Text.hs @@ -1540,8 +1540,7 @@ initsNE t = empty NonEmptyList.:| case t of -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. tails :: Text -> [Text] -tails t | null t = [empty] - | otherwise = t : tails (unsafeTail t) +tails = NonEmptyList.toList . tailsNE -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. diff --git a/src/Data/Text/Lazy.hs b/src/Data/Text/Lazy.hs index cd80a003..ad5c8ebf 100644 --- a/src/Data/Text/Lazy.hs +++ b/src/Data/Text/Lazy.hs @@ -1442,10 +1442,7 @@ initsNE = (Empty NE.:|) . inits' -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. tails :: Text -> [Text] -tails Empty = Empty : [] -tails ts@(Chunk t ts') - | T.length t == 1 = ts : tails ts' - | otherwise = ts : tails (Chunk (T.unsafeTail t) ts') +tails = NE.toList . tailsNE -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. From 0b379bdedba18fd765c7a7bdd611541c3caab908 Mon Sep 17 00:00:00 2001 From: Ignat Insarov Date: Fri, 9 Feb 2024 15:42:18 +0700 Subject: [PATCH 08/14] Be compatible with older `base`. --- src/Data/Text.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/Text.hs b/src/Data/Text.hs index 88cfa7e1..ef194d3e 100644 --- a/src/Data/Text.hs +++ b/src/Data/Text.hs @@ -1546,7 +1546,7 @@ tails = NonEmptyList.toList . tailsNE -- first. tailsNE :: Text -> NonEmptyList.NonEmpty Text tailsNE t - | null t = NonEmptyList.singleton empty + | null t = empty NonEmptyList.:| [] | otherwise = t NonEmptyList.:| tails (unsafeTail t) -- $split From c0c25703276e8e3123da576697d336fef4ebf77d Mon Sep 17 00:00:00 2001 From: Ignat Insarov Date: Fri, 9 Feb 2024 15:42:52 +0700 Subject: [PATCH 09/14] `NonEmptyList.toList` is too lazy. --- src/Data/Text.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Data/Text.hs b/src/Data/Text.hs index ef194d3e..69e80a19 100644 --- a/src/Data/Text.hs +++ b/src/Data/Text.hs @@ -1525,7 +1525,7 @@ group = groupBy (==) -- | /O(n)/ Return all initial segments of the given 'Text', shortest -- first. inits :: Text -> [Text] -inits = NonEmptyList.toList . initsNE +inits = (NonEmptyList.toList $!) . initsNE -- | /O(n)/ Return all initial segments of the given 'Text', shortest -- first. @@ -1540,7 +1540,7 @@ initsNE t = empty NonEmptyList.:| case t of -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. tails :: Text -> [Text] -tails = NonEmptyList.toList . tailsNE +tails = (NonEmptyList.toList $!) . tailsNE -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. From fe45845447a31a59fc1e1ac90cb3bef164a8cf49 Mon Sep 17 00:00:00 2001 From: Ignat Insarov Date: Fri, 9 Feb 2024 16:41:54 +0700 Subject: [PATCH 10/14] fixup! `NonEmptyList.toList` is too lazy. --- src/Data/Text/Lazy.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Data/Text/Lazy.hs b/src/Data/Text/Lazy.hs index ad5c8ebf..9fcb208f 100644 --- a/src/Data/Text/Lazy.hs +++ b/src/Data/Text/Lazy.hs @@ -1429,7 +1429,7 @@ groupBy eq (Chunk t ts) = cons x ys : groupBy eq zs -- | /O(n)/ Return all initial segments of the given 'Text', -- shortest first. inits :: Text -> [Text] -inits = NE.toList . initsNE +inits = (NE.toList P.$!) . initsNE -- | /O(n)/ Return all initial segments of the given 'Text', -- shortest first. @@ -1442,7 +1442,7 @@ initsNE = (Empty NE.:|) . inits' -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. tails :: Text -> [Text] -tails = NE.toList . tailsNE +tails = (NE.toList P.$!) . tailsNE -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. From 4165f6a5c243c2a3e0e2500574b631520e8cd429 Mon Sep 17 00:00:00 2001 From: Ignat Insarov Date: Thu, 15 Feb 2024 12:48:00 +0700 Subject: [PATCH 11/14] fixup! Write test cases and stub functions. --- tests/Tests/ShareEmpty.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Tests/ShareEmpty.hs b/tests/Tests/ShareEmpty.hs index 7c4e1345..adaf700d 100644 --- a/tests/Tests/ShareEmpty.hs +++ b/tests/Tests/ShareEmpty.hs @@ -112,7 +112,7 @@ tests = testGroup "empty Text values are shared" , testCase "tails empty = [empty]" $ mapM_ assertPtrEqEmpty $ T.tails empty , testCase "tailsNE empty = singleton empty" $ mapM_ assertPtrEqEmpty $ T.tailsNE empty , testCase "tails _ = [..., empty]" $ assertPtrEqEmpty $ L.last $ T.tails "123" - , testCase "tailsNE _ = reverse (empty :| …)" $ assertPtrEqEmpty $ NonEmptyList.last $ T.tailsNE "123" + , testCase "tailsNE _ = reverse (empty :| ...)" $ assertPtrEqEmpty $ NonEmptyList.last $ T.tailsNE "123" , testCase "split _ empty = [empty]" $ mapM_ assertPtrEqEmpty $ T.split (== 'a') "" , testCase "filter (const False) _ = empty" $ assertPtrEqEmpty $ T.filter (const False) "1234" , testCase "zipWith const empty empty = empty" $ assertPtrEqEmpty $ T.zipWith const "" "" From 0458012c30ea7150c060e9613eb833e424a2cdcf Mon Sep 17 00:00:00 2001 From: Ignat Insarov Date: Thu, 15 Feb 2024 12:55:07 +0700 Subject: [PATCH 12/14] Add `since` metadata. --- src/Data/Text.hs | 4 ++++ src/Data/Text/Lazy.hs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/Data/Text.hs b/src/Data/Text.hs index 69e80a19..78e8efc4 100644 --- a/src/Data/Text.hs +++ b/src/Data/Text.hs @@ -1529,6 +1529,8 @@ inits = (NonEmptyList.toList $!) . initsNE -- | /O(n)/ Return all initial segments of the given 'Text', shortest -- first. +-- +-- @since 2.1.2 initsNE :: Text -> NonEmptyList.NonEmpty Text initsNE t = empty NonEmptyList.:| case t of Text arr off len -> @@ -1544,6 +1546,8 @@ tails = (NonEmptyList.toList $!) . tailsNE -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. +-- +-- @since 2.1.2 tailsNE :: Text -> NonEmptyList.NonEmpty Text tailsNE t | null t = empty NonEmptyList.:| [] diff --git a/src/Data/Text/Lazy.hs b/src/Data/Text/Lazy.hs index 9fcb208f..c52aa7a2 100644 --- a/src/Data/Text/Lazy.hs +++ b/src/Data/Text/Lazy.hs @@ -1433,6 +1433,8 @@ inits = (NE.toList P.$!) . initsNE -- | /O(n)/ Return all initial segments of the given 'Text', -- shortest first. +-- +-- @since 2.1.2 initsNE :: Text -> NonEmpty Text initsNE = (Empty NE.:|) . inits' where inits' Empty = [] @@ -1446,6 +1448,8 @@ tails = (NE.toList P.$!) . tailsNE -- | /O(n)/ Return all final segments of the given 'Text', longest -- first. +-- +-- @since 2.1.2 tailsNE :: Text -> NonEmpty Text tailsNE Empty = Empty :| [] tailsNE ts@(Chunk t ts') From 4faa72c0910c7492496fed44dd060660fdba1503 Mon Sep 17 00:00:00 2001 From: Ignat Insarov Date: Thu, 15 Feb 2024 15:55:27 +0300 Subject: [PATCH 13/14] Make sure everything inlines. L.drop does not inline because it is recursive. Co-authored-by: Xia Li-yao --- src/Data/Text/Lazy.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/Text/Lazy.hs b/src/Data/Text/Lazy.hs index c52aa7a2..b4d3e0a3 100644 --- a/src/Data/Text/Lazy.hs +++ b/src/Data/Text/Lazy.hs @@ -1438,7 +1438,7 @@ inits = (NE.toList P.$!) . initsNE initsNE :: Text -> NonEmpty Text initsNE = (Empty NE.:|) . inits' where inits' Empty = [] - inits' (Chunk t ts) = L.map (\t' -> Chunk t' Empty) (L.drop 1 (T.inits t)) + inits' (Chunk t ts) = L.map (\t' -> Chunk t' Empty) (NE.tail (T.initsNE t)) ++ L.map (Chunk t) (inits' ts) -- | /O(n)/ Return all final segments of the given 'Text', longest From fb8fca246e1456e1213d804576e9c79d93eb2c80 Mon Sep 17 00:00:00 2001 From: Ignat Insarov Date: Sat, 17 Feb 2024 08:36:02 +0700 Subject: [PATCH 14/14] Fix false complexity statements. --- src/Data/Text/Lazy.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Data/Text/Lazy.hs b/src/Data/Text/Lazy.hs index b4d3e0a3..7bbcf076 100644 --- a/src/Data/Text/Lazy.hs +++ b/src/Data/Text/Lazy.hs @@ -1426,12 +1426,12 @@ groupBy eq (Chunk t ts) = cons x ys : groupBy eq zs x = T.unsafeHead t xs = chunk (T.unsafeTail t) ts --- | /O(n)/ Return all initial segments of the given 'Text', +-- | /O(n²)/ Return all initial segments of the given 'Text', -- shortest first. inits :: Text -> [Text] inits = (NE.toList P.$!) . initsNE --- | /O(n)/ Return all initial segments of the given 'Text', +-- | /O(n²)/ Return all initial segments of the given 'Text', -- shortest first. -- -- @since 2.1.2