Skip to content

Commit 41cc152

Browse files
committed
Nix the Splittable class; add fromFunction
Write mapWithIndex using a hand-unboxed mapWithIndex#. Make `split` strict, and add an internal strict `splitAt'`. This helps `zipWith` a little.
1 parent c0e8c7d commit 41cc152

File tree

1 file changed

+167
-82
lines changed

1 file changed

+167
-82
lines changed

Data/Sequence.hs

Lines changed: 167 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
-- Module : Data.Sequence
2020
-- Copyright : (c) Ross Paterson 2005
2121
-- (c) Louis Wasserman 2009
22+
-- (c) David Feuer and Milan Straka 2014
2223
-- License : BSD-style
2324
-- Maintainer : [email protected]
2425
-- Stability : experimental
@@ -60,6 +61,7 @@ module Data.Sequence (
6061
(|>), -- :: Seq a -> a -> Seq a
6162
(><), -- :: Seq a -> Seq a -> Seq a
6263
fromList, -- :: [a] -> Seq a
64+
fromFunction, -- :: Int -> (Int -> a) -> Seq a
6365
-- ** Repetition
6466
replicate, -- :: Int -> a -> Seq a
6567
replicateA, -- :: Applicative f => Int -> f a -> f (Seq a)
@@ -128,7 +130,6 @@ module Data.Sequence (
128130
foldlWithIndex, -- :: (b -> Int -> a -> b) -> b -> Seq a -> b
129131
foldrWithIndex, -- :: (Int -> a -> b -> b) -> b -> Seq a -> b
130132
-- * Transformations
131-
genSplitTraverseSeq,
132133
mapWithIndex, -- :: (Int -> a -> b) -> Seq a -> Seq b
133134
reverse, -- :: Seq a -> Seq a
134135
-- ** Zips
@@ -177,13 +178,21 @@ import Text.Read (Lexeme(Ident), lexP, parens, prec,
177178
readPrec, readListPrec, readListPrecDefault)
178179
import Data.Data
179180
#endif
180-
#if __GLASGOW_HASKELL__ >= 709
181+
#if __GLASGOW_HASKELL__ >= 708
181182
import Data.Coerce
183+
#define COERCE coerce
184+
#else
185+
#ifdef __GLASGOW_HASKELL__
186+
import qualified Unsafe.Coerce
187+
-- Note that by compiling this file with GHC 7.8 or later, we prove that
188+
-- it is safe to use COERCE with earlier GHC versions.
189+
#define COERCE Unsafe.Coerce.unsafeCoerce
190+
#endif
182191
#endif
183192
#if MIN_VERSION_base(4,8,0)
184193
import Data.Functor.Identity (Identity(..))
185194
#endif
186-
#if __GLASGOW_HASKELL__ >= 708
195+
#ifdef __GLASGOW_HASKELL__
187196
import qualified GHC.Exts
188197
#endif
189198

@@ -1292,11 +1301,56 @@ adjustDigit f i (Four a b c d)
12921301
sab = sa + size b
12931302
sabc = sab + size c
12941303

1295-
-- | A generalization of 'fmap', 'mapWithIndex' takes a mapping function
1296-
-- that also depends on the element's index, and applies it to every
1304+
-- | /O(n)/. A generalization of 'fmap', 'mapWithIndex' takes a mapping
1305+
-- function that also depends on the element's index, and applies it to every
12971306
-- element in the sequence.
12981307
mapWithIndex :: (Int -> a -> b) -> Seq a -> Seq b
1299-
mapWithIndex f xs = snd (mapAccumL' (\ i x -> (i + 1, f i x)) 0 xs)
1308+
mapWithIndex f (Seq xs) = Seq $ mapWithIndexTree (\s (Elem a) -> Elem (f s a)) 0 xs
1309+
where
1310+
{-# SPECIALIZE mapWithIndexTree :: (Int -> Elem y -> b) -> Int -> FingerTree (Elem y) -> FingerTree b #-}
1311+
{-# SPECIALIZE mapWithIndexTree :: (Int -> Node y -> b) -> Int -> FingerTree (Node y) -> FingerTree b #-}
1312+
mapWithIndexTree :: Sized a => (Int -> a -> b) -> Int -> FingerTree a -> FingerTree b
1313+
mapWithIndexTree _f s Empty = s `seq` Empty
1314+
mapWithIndexTree f s (Single xs) = Single $ f s xs
1315+
mapWithIndexTree f s (Deep n pr m sf) = sPspr `seq` sPsprm `seq`
1316+
Deep n
1317+
(mapWithIndexDigit f s pr)
1318+
(mapWithIndexTree (mapWithIndexNode f) sPspr m)
1319+
(mapWithIndexDigit f sPsprm sf)
1320+
where
1321+
sPspr = s + size pr
1322+
sPsprm = s + n - size sf
1323+
1324+
{-# SPECIALIZE mapWithIndexDigit :: (Int -> Elem y -> b) -> Int -> Digit (Elem y) -> Digit b #-}
1325+
{-# SPECIALIZE mapWithIndexDigit :: (Int -> Node y -> b) -> Int -> Digit (Node y) -> Digit b #-}
1326+
mapWithIndexDigit :: Sized a => (Int -> a -> b) -> Int -> Digit a -> Digit b
1327+
mapWithIndexDigit f s (One a) = One (f s a)
1328+
mapWithIndexDigit f s (Two a b) = sPsa `seq` Two (f s a) (f sPsa b)
1329+
where
1330+
sPsa = s + size a
1331+
mapWithIndexDigit f s (Three a b c) = sPsa `seq` sPsab `seq`
1332+
Three (f s a) (f sPsa b) (f sPsab c)
1333+
where
1334+
sPsa = s + size a
1335+
sPsab = sPsa + size b
1336+
mapWithIndexDigit f s (Four a b c d) = sPsa `seq` sPsab `seq` sPsabc `seq`
1337+
Four (f s a) (f sPsa b) (f sPsab c) (f sPsabc d)
1338+
where
1339+
sPsa = s + size a
1340+
sPsab = sPsa + size b
1341+
sPsabc = sPsab + size c
1342+
1343+
{-# SPECIALIZE mapWithIndexNode :: (Int -> Elem y -> b) -> Int -> Node (Elem y) -> Node b #-}
1344+
{-# SPECIALIZE mapWithIndexNode :: (Int -> Node y -> b) -> Int -> Node (Node y) -> Node b #-}
1345+
mapWithIndexNode :: Sized a => (Int -> a -> b) -> Int -> Node a -> Node b
1346+
mapWithIndexNode f s (Node2 ns a b) = sPsa `seq` Node2 ns (f s a) (f sPsa b)
1347+
where
1348+
sPsa = s + size a
1349+
mapWithIndexNode f s (Node3 ns a b c) = sPsa `seq` sPsab `seq`
1350+
Node3 ns (f s a) (f sPsa b) (f sPsab c)
1351+
where
1352+
sPsa = s + size a
1353+
sPsab = sPsa + size b
13001354

13011355
#ifdef __GLASGOW_HASKELL__
13021356
{-# NOINLINE [1] mapWithIndex #-}
@@ -1310,35 +1364,70 @@ mapWithIndex f xs = snd (mapAccumL' (\ i x -> (i + 1, f i x)) 0 xs)
13101364
#-}
13111365
#endif
13121366

1367+
-- | /O(n)/. Convert a given sequence length and a function representing that
1368+
-- sequence into a sequence.
1369+
fromFunction :: Int -> (Int -> a) -> Seq a
1370+
fromFunction len f | len < 0 = error "Data.Sequence.fromFunction called with negative len"
1371+
| len == 0 = empty
1372+
#ifdef __GLASGOW_HASKELL__
1373+
| otherwise = Seq $ create (COERCE f) 1 0 len
1374+
#else
1375+
| otherwise = Seq $ create (Elem . f) 1 0 len
1376+
#endif
1377+
where
1378+
create :: (Int -> a) -> Int -> Int -> Int -> FingerTree a
1379+
create b{-tree_builder-} s{-tree_size-} i{-start_index-} trees = i `seq` s `seq` case trees of
1380+
1 -> Single $ b i
1381+
2 -> Deep (2*s) (One (b i)) Empty (One (b (i+s)))
1382+
3 -> Deep (3*s) (Two (b i) (b (i+s))) Empty (One (b (i+2*s)))
1383+
4 -> Deep (4*s) (Two (b i) (b (i+s))) Empty (Two (b (i+2*s)) (b (i+3*s)))
1384+
5 -> Deep (5*s) (Three (b i) (b (i+s)) (b (i+2*s))) Empty (Two (b (i+3*s)) (b (i+4*s)))
1385+
6 -> Deep (5*s) (Three (b i) (b (i+s)) (b (i+2*s))) Empty (Three (b (i+3*s)) (b (i+4*s)) (b (i+5*s)))
1386+
_ -> case trees `quotRem` 3 of
1387+
(trees',1) -> Deep (trees*s) (Two (b i) (b (i+s)))
1388+
(create (\j -> Node3 (3*s) (b j) (b (j+s)) (b (j+2*s))) (3*s) (i+2*s) (trees'-1))
1389+
(Two (b (i+(2+3*(trees'-1))*s)) (b (i+(3+3*(trees'-1))*s)))
1390+
(trees',2) -> Deep (trees*s) (Three (b i) (b (i+s)) (b (i+2*s)))
1391+
(create (\j -> Node3 (3*s) (b j) (b (j+s)) (b (j+2*s))) (3*s) (i+3*s) (trees'-1))
1392+
(Two (b (i+(3+3*(trees'-1))*s)) (b (i+(4+3*(trees'-1))*s)))
1393+
(trees',0) -> Deep (trees*s) (Three (b i) (b (i+s)) (b (i+2*s)))
1394+
(create (\j -> Node3 (3*s) (b j) (b (j+s)) (b (j+2*s))) (3*s) (i+3*s) (trees'-2))
1395+
(Three (b (i+(3+3*(trees'-2))*s)) (b (i+(4+3*(trees'-2))*s)) (b (i+(5+3*(trees'-2))*s)))
1396+
13131397
-- Splitting
13141398

13151399
-- | /O(log(min(i,n-i)))/. The first @i@ elements of a sequence.
13161400
-- If @i@ is negative, @'take' i s@ yields the empty sequence.
13171401
-- If the sequence contains fewer than @i@ elements, the whole sequence
13181402
-- is returned.
13191403
take :: Int -> Seq a -> Seq a
1320-
take i = fst . splitAt i
1404+
take i = fst . splitAt' i
13211405

13221406
-- | /O(log(min(i,n-i)))/. Elements of a sequence after the first @i@.
13231407
-- If @i@ is negative, @'drop' i s@ yields the whole sequence.
13241408
-- If the sequence contains fewer than @i@ elements, the empty sequence
13251409
-- is returned.
13261410
drop :: Int -> Seq a -> Seq a
1327-
drop i = snd . splitAt i
1411+
drop i = snd . splitAt' i
13281412

13291413
-- | /O(log(min(i,n-i)))/. Split a sequence at a given position.
13301414
-- @'splitAt' i s = ('take' i s, 'drop' i s)@.
13311415
splitAt :: Int -> Seq a -> (Seq a, Seq a)
13321416
splitAt i (Seq xs) = (Seq l, Seq r)
13331417
where (l, r) = split i xs
13341418

1419+
-- | /O(log(min(i,n-i))) A strict version of 'splitAt'.
1420+
splitAt' :: Int -> Seq a -> (Seq a, Seq a)
1421+
splitAt' i (Seq xs) = case split i xs of
1422+
(l, r) -> (Seq l, Seq r)
1423+
13351424
split :: Int -> FingerTree (Elem a) ->
13361425
(FingerTree (Elem a), FingerTree (Elem a))
13371426
split i Empty = i `seq` (Empty, Empty)
13381427
split i xs
1339-
| size xs > i = (l, consTree x r)
1428+
| size xs > i = case splitTree i xs of
1429+
Split l x r -> (l, consTree x r)
13401430
| otherwise = (xs, Empty)
1341-
where Split l x r = splitTree i xs
13421431

13431432
data Split t a = Split t a t
13441433
#if TESTING
@@ -1704,7 +1793,7 @@ reverseNode f (Node2 s a b) = Node2 s (f b) (f a)
17041793
reverseNode f (Node3 s a b c) = Node3 s (f c) (f b) (f a)
17051794

17061795
------------------------------------------------------------------------
1707-
-- Traversing with splittable "state"
1796+
-- Mapping with a splittable value
17081797
------------------------------------------------------------------------
17091798

17101799
-- For zipping, and probably also for (<*>), it is useful to build a result by
@@ -1765,70 +1854,68 @@ reverseNode f (Node3 s a b c) = Node3 s (f c) (f b) (f a)
17651854
--
17661855
-- David Feuer, with excellent guidance from Carter Schonwald, December 2014
17671856

1768-
class Splittable s where
1769-
splitState :: Int -> s -> (s,s)
1770-
1771-
instance Splittable (Seq a) where
1772-
splitState = splitAt
1773-
1774-
instance (Splittable a, Splittable b) => Splittable (a, b) where
1775-
splitState i (a, b) = (al `seq` bl `seq` (al, bl), ar `seq` br `seq` (ar, br))
1776-
where
1777-
(al, ar) = splitState i a
1778-
(bl, br) = splitState i b
1779-
1780-
data GenSplittable s = GenSplittable s (Int -> s -> (s,s))
1781-
instance Splittable (GenSplittable s) where
1782-
splitState i (GenSplittable s spl) = (GenSplittable l spl, GenSplittable r spl)
1783-
where
1784-
(l,r) = spl i s
1785-
1786-
{-# INLINE genSplitTraverseSeq #-}
1787-
genSplitTraverseSeq :: (Int -> s -> (s, s)) -> (s -> a -> b) -> s -> Seq a -> Seq b
1788-
genSplitTraverseSeq spl f s = splitTraverseSeq (\(GenSplittable s _) -> f s) (GenSplittable s spl)
1789-
1790-
{-# SPECIALIZE splitTraverseSeq :: (Seq x -> a -> b) -> Seq x -> Seq a -> Seq b #-}
1791-
{-# SPECIALIZE splitTraverseSeq :: ((Seq x, Seq y) -> a -> b) -> (Seq x, Seq y) -> Seq a -> Seq b #-}
1792-
splitTraverseSeq :: (Splittable s) => (s -> a -> b) -> s -> Seq a -> Seq b
1793-
splitTraverseSeq f s (Seq xs) = Seq $ splitTraverseTree (\s' (Elem a) -> Elem (f s' a)) s xs
1794-
1795-
{-# SPECIALIZE splitTraverseTree :: (Seq x -> Elem y -> b) -> Seq x -> FingerTree (Elem y) -> FingerTree b #-}
1796-
{-# SPECIALIZE splitTraverseTree :: (Seq x -> Node y -> b) -> Seq x -> FingerTree (Node y) -> FingerTree b #-}
1797-
splitTraverseTree :: (Sized a, Splittable s) => (s -> a -> b) -> s -> FingerTree a -> FingerTree b
1798-
splitTraverseTree _f _s Empty = Empty
1799-
splitTraverseTree f s (Single xs) = Single $ f s xs
1800-
splitTraverseTree f s (Deep n pr m sf) = Deep n (splitTraverseDigit f prs pr) (splitTraverseTree (splitTraverseNode f) ms m) (splitTraverseDigit f sfs sf)
1801-
where
1802-
(prs, r) = splitState (size pr) s
1803-
(ms, sfs) = splitState (n - size pr - size sf) r
1804-
1805-
{-# SPECIALIZE splitTraverseDigit :: (Seq x -> Elem y -> b) -> Seq x -> Digit (Elem y) -> Digit b #-}
1806-
{-# SPECIALIZE splitTraverseDigit :: (Seq x -> Node y -> b) -> Seq x -> Digit (Node y) -> Digit b #-}
1807-
splitTraverseDigit :: (Sized a, Splittable s) => (s -> a -> b) -> s -> Digit a -> Digit b
1808-
splitTraverseDigit f s (One a) = One (f s a)
1809-
splitTraverseDigit f s (Two a b) = Two (f first a) (f second b)
1810-
where
1811-
(first, second) = splitState (size a) s
1812-
splitTraverseDigit f s (Three a b c) = Three (f first a) (f second b) (f third c)
1813-
where
1814-
(first, r) = splitState (size a) s
1815-
(second, third) = splitState (size b) r
1816-
splitTraverseDigit f s (Four a b c d) = Four (f first a) (f second b) (f third c) (f fourth d)
1817-
where
1818-
(first, s') = splitState (size a) s
1819-
(middle, fourth) = splitState (size b + size c) s'
1820-
(second, third) = splitState (size b) middle
1821-
1822-
{-# SPECIALIZE splitTraverseNode :: (Seq x -> Elem y -> b) -> Seq x -> Node (Elem y) -> Node b #-}
1823-
{-# SPECIALIZE splitTraverseNode :: (Seq x -> Node y -> b) -> Seq x -> Node (Node y) -> Node b #-}
1824-
splitTraverseNode :: (Sized a, Splittable s) => (s -> a -> b) -> s -> Node a -> Node b
1825-
splitTraverseNode f s (Node2 ns a b) = Node2 ns (f first a) (f second b)
1826-
where
1827-
(first, second) = splitState (size a) s
1828-
splitTraverseNode f s (Node3 ns a b c) = Node3 ns (f first a) (f second b) (f third c)
1829-
where
1830-
(first, r) = splitState (size a) s
1831-
(second, third) = splitState (size b) r
1857+
-- | /O(n)/. Constructs a new sequence with the same structure as an existing
1858+
-- sequence using a user-supplied mapping function along with a splittable
1859+
-- value and a way to split it. The value is split up lazily according to the
1860+
-- structure of the sequence, so one piece of the value is distributed to each
1861+
-- element of the sequence. The caller should provide a splitter function that
1862+
-- takes a number, @n@, and a splittable value, breaks off a chunk of size @n@
1863+
-- from the value, and returns that chunk and the remainder as a pair. The
1864+
-- following examples will hopefully make the usage clear:
1865+
--
1866+
-- > zipWith :: (a -> b -> c) -> Seq a -> Seq b -> Seq c
1867+
-- > zipWith f s1 s2 = splitMap splitAt (\b a -> f a (b `index` 0)) s2' s1'
1868+
-- > where
1869+
-- > minLen = min (length s1) (length s2)
1870+
-- > s1' = take minLen s1
1871+
-- > s2' = take minLen s2
1872+
--
1873+
-- > mapWithIndex :: (Int -> a -> b) -> Seq a -> Seq b
1874+
-- > mapWithIndex f = splitMap (\n i -> (i, n+i)) f 0
1875+
splitMap :: (Int -> s -> (s,s)) -> (s -> a -> b) -> s -> Seq a -> Seq b
1876+
splitMap splt' = go
1877+
where
1878+
go f s (Seq xs) = Seq $ splitMapTree splt' (\s' (Elem a) -> Elem (f s' a)) s xs
1879+
1880+
{-# SPECIALIZE splitMapTree :: (Int -> s -> (s,s)) -> (s -> Elem y -> b) -> s -> FingerTree (Elem y) -> FingerTree b #-}
1881+
{-# SPECIALIZE splitMapTree :: (Int -> s -> (s,s)) -> (s -> Node y -> b) -> s -> FingerTree (Node y) -> FingerTree b #-}
1882+
splitMapTree :: Sized a => (Int -> s -> (s,s)) -> (s -> a -> b) -> s -> FingerTree a -> FingerTree b
1883+
splitMapTree splt _f _s Empty = Empty
1884+
splitMapTree splt f s (Single xs) = Single $ f s xs
1885+
splitMapTree splt f s (Deep n pr m sf) = Deep n (splitMapDigit splt f prs pr) (splitMapTree splt (splitMapNode splt f) ms m) (splitMapDigit splt f sfs sf)
1886+
where
1887+
(prs, r) = splt (size pr) s
1888+
(ms, sfs) = splt (n - size pr - size sf) r
1889+
1890+
{-# SPECIALIZE splitMapDigit :: (Int -> s -> (s,s)) -> (s -> Elem y -> b) -> s -> Digit (Elem y) -> Digit b #-}
1891+
{-# SPECIALIZE splitMapDigit :: (Int -> s -> (s,s)) -> (s -> Node y -> b) -> s -> Digit (Node y) -> Digit b #-}
1892+
splitMapDigit :: Sized a => (Int -> s -> (s,s)) -> (s -> a -> b) -> s -> Digit a -> Digit b
1893+
splitMapDigit splt f s (One a) = One (f s a)
1894+
splitMapDigit splt f s (Two a b) = Two (f first a) (f second b)
1895+
where
1896+
(first, second) = splt (size a) s
1897+
splitMapDigit splt f s (Three a b c) = Three (f first a) (f second b) (f third c)
1898+
where
1899+
(first, r) = splt (size a) s
1900+
(second, third) = splt (size b) r
1901+
splitMapDigit splt f s (Four a b c d) = Four (f first a) (f second b) (f third c) (f fourth d)
1902+
where
1903+
(first, s') = splt (size a) s
1904+
(middle, fourth) = splt (size b + size c) s'
1905+
(second, third) = splt (size b) middle
1906+
1907+
{-# SPECIALIZE splitMapNode :: (Int -> s -> (s,s)) -> (s -> Elem y -> b) -> s -> Node (Elem y) -> Node b #-}
1908+
{-# SPECIALIZE splitMapNode :: (Int -> s -> (s,s)) -> (s -> Node y -> b) -> s -> Node (Node y) -> Node b #-}
1909+
splitMapNode :: Sized a => (Int -> s -> (s,s)) -> (s -> a -> b) -> s -> Node a -> Node b
1910+
splitMapNode splt f s (Node2 ns a b) = Node2 ns (f first a) (f second b)
1911+
where
1912+
(first, second) = splt (size a) s
1913+
splitMapNode splt f s (Node3 ns a b c) = Node3 ns (f first a) (f second b) (f third c)
1914+
where
1915+
(first, r) = splt (size a) s
1916+
(second, third) = splt (size b) r
1917+
1918+
{-# INLINE splitMap #-}
18321919

18331920
getSingleton :: Seq a -> a
18341921
getSingleton (Seq (Single (Elem a))) = a
@@ -1850,7 +1937,7 @@ zip = zipWith (,)
18501937
-- For example, @zipWith (+)@ is applied to two sequences to take the
18511938
-- sequence of corresponding sums.
18521939
zipWith :: (a -> b -> c) -> Seq a -> Seq b -> Seq c
1853-
zipWith f s1 s2 = splitTraverseSeq (\s a -> f a (getSingleton s)) s2' s1'
1940+
zipWith f s1 s2 = splitMap splitAt' (\s a -> f a (getSingleton s)) s2' s1'
18541941
where
18551942
minLen = min (length s1) (length s2)
18561943
s1' = take minLen s1
@@ -1865,9 +1952,8 @@ zip3 = zipWith3 (,,)
18651952
-- three elements, as well as three sequences and returns a sequence of
18661953
-- their point-wise combinations, analogous to 'zipWith'.
18671954
zipWith3 :: (a -> b -> c -> d) -> Seq a -> Seq b -> Seq c -> Seq d
1868-
zipWith3 f s1 s2 s3 = splitTraverseSeq (\s a ->
1869-
case s of
1870-
(b, c) -> f a (getSingleton b) (getSingleton c)) (s2', s3') s1'
1955+
zipWith3 f s1 s2 s3 = splitMap (\i (s,t) -> case (splitAt' i s, splitAt' i t) of ((s', s''), (t', t'')) -> ((s',t'),(s'',t'')))
1956+
(\(b,c) a -> f a (getSingleton b) (getSingleton c)) (s2',s3') s1'
18711957
where
18721958
minLen = minimum [length s1, length s2, length s3]
18731959
s1' = take minLen s1
@@ -1883,9 +1969,8 @@ zip4 = zipWith4 (,,,)
18831969
-- four elements, as well as four sequences and returns a sequence of
18841970
-- their point-wise combinations, analogous to 'zipWith'.
18851971
zipWith4 :: (a -> b -> c -> d -> e) -> Seq a -> Seq b -> Seq c -> Seq d -> Seq e
1886-
zipWith4 f s1 s2 s3 s4 = splitTraverseSeq (\s a ->
1887-
case s of
1888-
(b, (c, d)) -> f a (getSingleton b) (getSingleton c) (getSingleton d)) (s2', (s3', s4')) s1'
1972+
zipWith4 f s1 s2 s3 s4 = splitMap (\i (s,t,u) -> case (splitAt' i s, splitAt' i t, splitAt' i u) of ((s',s''),(t',t''),(u',u'')) -> ((s',t',u'),(s'',t'',u'')))
1973+
(\(b, c, d) a -> f a (getSingleton b) (getSingleton c) (getSingleton d)) (s2',s3',s4') s1'
18891974
where
18901975
minLen = minimum [length s1, length s2, length s3, length s4]
18911976
s1' = take minLen s1

0 commit comments

Comments
 (0)