packages feed

primitive-sort 0.1.0.0 → 0.1.1.0

raw patch · 3 files changed

+66/−347 lines, 3 filesdep ~contiguousdep ~primitivePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: contiguous, primitive

API changes (from Hackage documentation)

- Data.Primitive.Sort: sortTaggedMutable :: (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v) => Mutable karr s k -> Mutable varr s v -> ST s (Mutable karr s k, Mutable varr s v)
+ Data.Primitive.Sort: sortTaggedMutable :: (ContiguousU karr, Element karr k, Ord k, ContiguousU varr, Element varr v) => Mutable karr s k -> Mutable varr s v -> ST s (Mutable karr s k, Mutable varr s v)
- Data.Primitive.Sort: sortUnique :: (Contiguous arr, Element arr a, Ord a) => arr a -> arr a
+ Data.Primitive.Sort: sortUnique :: (ContiguousU arr, Element arr a, Ord a) => arr a -> arr a
- Data.Primitive.Sort: sortUniqueMutable :: (Contiguous arr, Element arr a, Ord a) => Mutable arr s a -> ST s (Mutable arr s a)
+ Data.Primitive.Sort: sortUniqueMutable :: (ContiguousU arr, Element arr a, Ord a) => Mutable arr s a -> ST s (Mutable arr s a)
- Data.Primitive.Sort: sortUniqueTagged :: forall k v karr varr. (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v) => karr k -> varr v -> (karr k, varr v)
+ Data.Primitive.Sort: sortUniqueTagged :: forall k v karr varr. (ContiguousU karr, Element karr k, Ord k, ContiguousU varr, Element varr v) => karr k -> varr v -> (karr k, varr v)
- Data.Primitive.Sort: sortUniqueTaggedMutable :: (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v) => Mutable karr s k -> Mutable varr s v -> ST s (Mutable karr s k, Mutable varr s v)
+ Data.Primitive.Sort: sortUniqueTaggedMutable :: (ContiguousU karr, Element karr k, Ord k, ContiguousU varr, Element varr v) => Mutable karr s k -> Mutable varr s v -> ST s (Mutable karr s k, Mutable varr s v)

Files

README.md view
@@ -1,1 +1,3 @@-# mergesort+# primitive-sort++sorting of contiguous data structures.
primitive-sort.cabal view
@@ -1,6 +1,6 @@-cabal-version: 2.0+cabal-version: 2.2 name: primitive-sort-version: 0.1.0.0+version: 0.1.1.0 synopsis: Sort primitive arrays description:   This library provides a stable sorting algorithm for primitive arrays.@@ -10,7 +10,7 @@   to insertion sort on small chunks. The are also novel improvements   to increase the performance if the input array is already mostly sorted. homepage: https://github.com/andrewthad/primitive-sort-license: BSD3+license: BSD-3-Clause license-file: LICENSE author: Andrew Martin maintainer: andrew.thaddeus@gmail.com@@ -24,10 +24,10 @@   exposed-modules:     Data.Primitive.Sort   build-depends:-      base >= 0.4.9 && < 5-    , primitive >= 0.6.4.0+    , base >= 0.4.9 && < 5     , ghc-prim-    , contiguous >= 0.1 && < 0.2+    , contiguous >= 0.6 && < 0.7+    , primitive >= 0.6.4 && < 0.8   ghc-options: -O2   default-language: Haskell2010 
src/Data/Primitive/Sort.hs view
@@ -25,13 +25,9 @@  import Control.Monad.ST import Control.Applicative-import GHC.ST (ST(..))-import GHC.IO (IO(..)) import GHC.Int (Int(..))-import Control.Monad import GHC.Prim-import Control.Concurrent (getNumCapabilities)-import Data.Primitive.Contiguous (Contiguous,Mutable,Element)+import Data.Primitive.Contiguous (Contiguous,ContiguousU,Mutable,Element) import qualified Data.Primitive.Contiguous as C  -- | Sort an immutable array. Duplicate elements are preserved.@@ -41,11 +37,11 @@ sort :: (Contiguous arr, Element arr a, Ord a)   => arr a   -> arr a-{-# INLINABLE sort #-}+{-# INLINE sort #-} sort !src = runST $ do   let len = C.size src   dst <- C.new (C.size src)-  C.copy dst 0 src 0 len+  C.copy dst 0 (C.slice src 0 len)   res <- sortMutable dst   C.unsafeFreeze res @@ -65,13 +61,13 @@   => karr k -- ^ keys   -> varr v -- ^ values   -> (karr k,varr v)-{-# INLINABLE sortTagged #-}+{-# INLINE sortTagged #-} sortTagged !src !srcTags = runST $ do   let len = min (C.size src) (C.size srcTags)   dst <- C.new len-  C.copy dst 0 src 0 len+  C.copy dst 0 (C.slice src 0 len)   dstTags <- C.new len-  C.copy dstTags 0 srcTags 0 len+  C.copy dstTags 0 (C.slice srcTags 0 len)   (res,resTags) <- sortTaggedMutableN len dst dstTags   res' <- C.unsafeFreeze res   resTags' <- C.unsafeFreeze resTags@@ -85,17 +81,17 @@ -- -- >>> sortUniqueTagged ([5,6,7,5,5,7] :: Array Int) ([1,2,3,4,5,6] :: Array Int) -- (fromListN 3 [5,6,7],fromListN 3 [5,2,6])-sortUniqueTagged :: forall k v karr varr. (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v)+sortUniqueTagged :: forall k v karr varr. (ContiguousU karr, Element karr k, Ord k, ContiguousU varr, Element varr v)   => karr k -- ^ keys   -> varr v -- ^ values   -> (karr k,varr v)-{-# INLINABLE sortUniqueTagged #-}+{-# INLINE sortUniqueTagged #-} sortUniqueTagged !src !srcTags = runST $ do   let len = min (C.size src) (C.size srcTags)   dst <- C.new len-  C.copy dst 0 src 0 len+  C.copy dst 0 (C.slice src 0 len)   dstTags <- C.new len-  C.copy dstTags 0 srcTags 0 len+  C.copy dstTags 0 (C.slice srcTags 0 len)   (res0,resTags0) <- sortTaggedMutableN len dst dstTags   (res1,resTags1) <- uniqueTaggedMutableN len res0 resTags0   res' <- C.unsafeFreeze res1@@ -109,47 +105,38 @@ sortMutable :: (Contiguous arr, Element arr a, Ord a)   => Mutable arr s a   -> ST s (Mutable arr s a)-{-# INLINABLE sortMutable #-}+{-# INLINE sortMutable #-} sortMutable !dst = do-  len <- C.sizeMutable dst+  len <- C.sizeMut dst   if len < threshold     then insertionSortRange dst 0 len     else do       work <- C.new len-      C.copyMutable work 0 dst 0 len -      caps <- unsafeEmbedIO getNumCapabilities-      let minElemsPerThread = 20000-          maxThreads = unsafeQuot len minElemsPerThread-          preThreads = min caps maxThreads-          threads = if preThreads == 1 then 1 else preThreads * 8-      -- I cannot understand why, but GHC's runtime does better-      -- when we let this schedule 8 times as many threads as-      -- we have capabilities. However, we only get this benefit-      -- when we actually have more than one capability.-      splitMergeParallel dst work threads 0 len+      C.copyMut work 0 (C.sliceMut dst 0 len)+      splitMerge dst work 0 len   return dst  -- | Sort an array of a key type @k@, rearranging the values of -- type @v@ according to the element they correspond to in the -- key array. The argument arrays may not be reused after they -- are passed to the function.-sortTaggedMutable :: (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v)+sortTaggedMutable :: (ContiguousU karr, Element karr k, Ord k, ContiguousU varr, Element varr v)   => Mutable karr s k   -> Mutable varr s v   -> ST s (Mutable karr s k, Mutable varr s v)-{-# INLINABLE sortTaggedMutable #-}+{-# INLINE sortTaggedMutable #-} sortTaggedMutable !dst0 !dstTags0 = do   (!dst,!dstTags,!len) <- alignArrays dst0 dstTags0   sortTaggedMutableN len dst dstTags -alignArrays :: (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v)+alignArrays :: (ContiguousU karr, Element karr k, Ord k, ContiguousU varr, Element varr v)   => Mutable karr s k   -> Mutable varr s v   -> ST s (Mutable karr s k, Mutable varr s v,Int)-{-# INLINABLE alignArrays #-}+{-# INLINE alignArrays #-} alignArrays dst0 dstTags0 = do-  lenDst <- C.sizeMutable dst0-  lenDstTags <- C.sizeMutable dstTags0+  lenDst <- C.sizeMut dst0+  lenDstTags <- C.sizeMut dstTags0   -- This cleans up mismatched lengths.   if lenDst == lenDstTags     then return (dst0,dstTags0,lenDst)@@ -161,11 +148,11 @@         dst <- C.resize dst0 lenDstTags         return (dst,dstTags0,lenDstTags) -sortUniqueTaggedMutable :: (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v)+sortUniqueTaggedMutable :: (ContiguousU karr, Element karr k, Ord k, ContiguousU varr, Element varr v)   => Mutable karr s k -- ^ keys   -> Mutable varr s v -- ^ values   -> ST s (Mutable karr s k, Mutable varr s v)-{-# INLINABLE sortUniqueTaggedMutable #-}+{-# INLINE sortUniqueTaggedMutable #-} sortUniqueTaggedMutable dst0 dstTags0 = do   (!dst1,!dstTags1,!len) <- alignArrays dst0 dstTags0   (!dst2,!dstTags2) <- sortTaggedMutableN len dst1 dstTags1@@ -176,20 +163,15 @@   -> Mutable karr s k   -> Mutable varr s v   -> ST s (Mutable karr s k, Mutable varr s v)-{-# INLINABLE sortTaggedMutableN #-}+{-# INLINE sortTaggedMutableN #-} sortTaggedMutableN !len !dst !dstTags = if len < thresholdTagged   then do     insertionSortTaggedRange dst dstTags 0 len     return (dst,dstTags)   else do-    work <- C.cloneMutable dst 0 len -    workTags <- C.cloneMutable dstTags 0 len -    caps <- unsafeEmbedIO getNumCapabilities-    let minElemsPerThread = 20000-        maxThreads = unsafeQuot len minElemsPerThread-        preThreads = min caps maxThreads-        threads = if preThreads == 1 then 1 else preThreads * 8-    splitMergeParallelTagged dst work dstTags workTags threads 0 len+    work <- C.cloneMut (C.sliceMut dst 0 len)+    workTags <- C.cloneMut (C.sliceMut dstTags 0 len)+    splitMergeTagged dst work dstTags workTags 0 len     return (dst,dstTags)  -- | Sort an immutable array. Only a single copy of each duplicated@@ -197,13 +179,13 @@ -- -- >>> sortUnique ([5,6,7,9,5,4,5,7] :: Array Int) -- fromListN 5 [4,5,6,7,9]-sortUnique :: (Contiguous arr, Element arr a, Ord a)+sortUnique :: (ContiguousU arr, Element arr a, Ord a)   => arr a -> arr a-{-# INLINABLE sortUnique #-}+{-# INLINE sortUnique #-} sortUnique src = runST $ do   let len = C.size src   dst <- C.new len-  C.copy dst 0 src 0 len+  C.copy dst 0 (C.slice src 0 len)   res <- sortUniqueMutable dst   C.unsafeFreeze res @@ -211,10 +193,10 @@ -- element is preserved. This operation may run in-place, or it may -- need to allocate a new array, so the argument may not be reused -- after this function is applied to it. -sortUniqueMutable :: (Contiguous arr, Element arr a, Ord a)+sortUniqueMutable :: (ContiguousU arr, Element arr a, Ord a)   => Mutable arr s a   -> ST s (Mutable arr s a)-{-# INLINABLE sortUniqueMutable #-}+{-# INLINE sortUniqueMutable #-} sortUniqueMutable marr = do   res <- sortMutable marr   uniqueMutable res@@ -222,11 +204,11 @@ -- | Discards adjacent equal elements from an array. This operation -- may run in-place, or it may need to allocate a new array, so the -- argument may not be reused after this function is applied to it.-uniqueMutable :: forall arr s a. (Contiguous arr, Element arr a, Eq a)+uniqueMutable :: forall arr s a. (ContiguousU arr, Element arr a, Eq a)   => Mutable arr s a -> ST s (Mutable arr s a)-{-# INLINABLE uniqueMutable #-}+{-# INLINE uniqueMutable #-} uniqueMutable !marr = do-  !len <- C.sizeMutable marr+  !len <- C.sizeMut marr   if len > 1     then do       !a0 <- C.read marr 0@@ -257,12 +239,12 @@           C.resize marr reducedLen     else return marr -uniqueTaggedMutableN :: forall karr varr s k v. (Contiguous karr, Element karr k, Eq k, Contiguous varr, Element varr v)+uniqueTaggedMutableN :: forall karr varr s k v. (ContiguousU karr, Element karr k, Eq k, ContiguousU varr, Element varr v)   => Int   -> Mutable karr s k   -> Mutable varr s v   -> ST s (Mutable karr s k, Mutable varr s v)-{-# INLINABLE uniqueTaggedMutableN #-}+{-# INLINE uniqueTaggedMutableN #-} uniqueTaggedMutableN !len !marr !marrTags = if len > 1   then do     !a0 <- C.read marr 0@@ -297,59 +279,13 @@         liftA2 (,) (C.resize marr reducedLen) (C.resize marrTags reducedLen)   else return (marr,marrTags) -unsafeEmbedIO :: IO a -> ST s a-unsafeEmbedIO (IO f) = ST (unsafeCoerce# f)--half :: Int -> Int-half x = unsafeQuot x 2--splitMergeParallel :: forall arr s a. (Contiguous arr, Element arr a, Ord a)-  => Mutable arr s a -- source and destination-  -> Mutable arr s a -- work array-  -> Int -- spark limit, should be power of two-  -> Int -- start-  -> Int -- end-  -> ST s ()-{-# INLINABLE splitMergeParallel #-}-splitMergeParallel !arr !work !level !start !end = if level > 1-  then if end - start < threshold-    then insertionSortRange arr start end-    else do-      let !mid = unsafeQuot (end + start) 2-          !levelDown = half level-      tandem -        (splitMergeParallel work arr levelDown start mid)-        (splitMergeParallel work arr levelDown mid end)-      mergeParallel work arr level start mid end-  else splitMerge arr work start end--splitMergeParallelTagged :: forall karr varr s k v. (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v)-  => Mutable karr s k -- source and destination-  -> Mutable karr s k -- work array-  -> Mutable varr s v -- source and destination tags-  -> Mutable varr s v -- work tags-  -> Int -- spark limit, should be power of two-  -> Int -- start-  -> Int -- end-  -> ST s ()-{-# INLINABLE splitMergeParallelTagged #-}-splitMergeParallelTagged !arr !work !arrTags !workTags !level !start !end = if level > 1-  then do-    let !mid = unsafeQuot (end + start) 2-        !levelDown = half level-    tandem -      (splitMergeParallelTagged work arr workTags arrTags levelDown start mid)-      (splitMergeParallelTagged work arr workTags arrTags levelDown mid end)-    mergeParallelTagged work arr workTags arrTags level start mid end-  else splitMergeTagged arr work arrTags workTags start end- splitMerge :: forall arr s a. (Contiguous arr, Element arr a, Ord a)   => Mutable arr s a -- source and destination   -> Mutable arr s a -- work array   -> Int -- start   -> Int -- end   -> ST s ()-{-# INLINABLE splitMerge #-}+{-# INLINE splitMerge #-} splitMerge !arr !work !start !end = if end - start < 2   then return ()   else if end - start > threshold@@ -368,7 +304,7 @@   -> Int -- start   -> Int -- end   -> ST s ()-{-# INLINABLE splitMergeTagged #-}+{-# INLINE splitMergeTagged #-} splitMergeTagged !arr !work !arrTags !workTags !start !end = if end - start < 2   then return ()   else if end - start > thresholdTagged@@ -379,196 +315,9 @@       mergeNonContiguousTagged work arr workTags arrTags start mid mid end start     else insertionSortTaggedRange arr arrTags start end --- Precondition: threads is greater than 0-mergeParallel :: forall arr s a. (Contiguous arr, Element arr a, Ord a)-  => Mutable arr s a -- source-  -> Mutable arr s a -- dest-  -> Int -- threads-  -> Int -- start-  -> Int -- middle-  -> Int -- end-  -> ST s ()-{-# INLINABLE mergeParallel #-}-mergeParallel !src !dst !threads !start !mid !end = do-  !lock <- newLock-  let go :: Int -- previous A end-         -> Int -- previous B end-         -> Int -- how many chunk have we already iterated over-         -> ST s Int-      go !prevEndA !prevEndB !ix = -        if | prevEndA == mid && prevEndB == end -> return ix-           | prevEndA == mid -> do-               forkST_ $ do-                 let !startA = mid-                     !endA = mid-                     !startB = prevEndB-                     !endB = end-                     !startDst = (startA - start) + (startB - mid) + start-                 mergeNonContiguous src dst startA endA startB endB startDst-                 putLock lock-               go mid end (ix + 1)-           | prevEndB == end -> do-               forkST_ $ do-                 let !startA = prevEndA-                     !endA = mid-                     !startB = end-                     !endB = end-                     !startDst = (startA - start) + (startB - mid) + start-                 mergeNonContiguous src dst startA endA startB endB startDst-                 putLock lock-               go mid end (ix + 1)-           | ix == threads - 1 -> do-               forkST_ $ do-                 let !startA = prevEndA-                     !endA = mid-                     !startB = prevEndB-                     !endB = end-                     !startDst = (startA - start) + (startB - mid) + start-                 mergeNonContiguous src dst startA endA startB endB startDst-                 putLock lock-               return (ix + 1)-           | otherwise -> do-               -- We use the left half for this lookup. We could instead-               -- use both halves and take the median.-               !endElem <- C.read src (start + chunk * (ix + 1))-               !endA <- findIndexOfGtElem src (endElem :: a) prevEndA mid-               !endB <- findIndexOfGtElem src endElem prevEndB end-               forkST_ $ do-                 let !startA = prevEndA-                     !startB = prevEndB-                     !startDst = (startA - start) + (startB - mid) + start-                 mergeNonContiguous src dst startA endA startB endB startDst-                 putLock lock-               go endA endB (ix + 1)-  !endElem <- C.read src (start + chunk) -  !endA <- findIndexOfGtElem src (endElem :: a) start mid-  !endB <- findIndexOfGtElem src endElem mid end-  forkST_ $ do-    let !startA = start-        !startB = mid-        !startDst = (startA - start) + (startB - mid) + start-    mergeNonContiguous src dst startA endA startB endB startDst-    putLock lock-  total <- go endA endB 1-  replicateM_ total (takeLock lock)-  where-  !chunk = unsafeQuot (end - start) threads---- Precondition: threads is greater than 0--- This function is just a copy of mergeParallel but with--- the tags arrays passed to mergeNonContiguousTagged-mergeParallelTagged :: forall karr varr s k v. (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v)-  => Mutable karr s k -- source-  -> Mutable karr s k -- dest-  -> Mutable varr s v -- source tags-  -> Mutable varr s v -- dest tags-  -> Int -- threads-  -> Int -- start-  -> Int -- middle-  -> Int -- end-  -> ST s ()-{-# INLINABLE mergeParallelTagged #-}-mergeParallelTagged !src !dst !srcTags !dstTags !threads !start !mid !end = do-  !lock <- newLock-  let go :: Int -- previous A end-         -> Int -- previous B end-         -> Int -- how many chunk have we already iterated over-         -> ST s Int-      go !prevEndA !prevEndB !ix = -        if | prevEndA == mid && prevEndB == end -> return ix-           | prevEndA == mid -> do-               forkST_ $ do-                 let !startA = mid-                     !endA = mid-                     !startB = prevEndB-                     !endB = end-                     !startDst = (startA - start) + (startB - mid) + start-                 mergeNonContiguousTagged src dst srcTags dstTags startA endA startB endB startDst-                 putLock lock-               go mid end (ix + 1)-           | prevEndB == end -> do-               forkST_ $ do-                 let !startA = prevEndA-                     !endA = mid-                     !startB = end-                     !endB = end-                     !startDst = (startA - start) + (startB - mid) + start-                 mergeNonContiguousTagged src dst srcTags dstTags startA endA startB endB startDst-                 putLock lock-               go mid end (ix + 1)-           | ix == threads - 1 -> do-               forkST_ $ do-                 let !startA = prevEndA-                     !endA = mid-                     !startB = prevEndB-                     !endB = end-                     !startDst = (startA - start) + (startB - mid) + start-                 mergeNonContiguousTagged src dst srcTags dstTags startA endA startB endB startDst-                 putLock lock-               return (ix + 1)-           | otherwise -> do-               -- We use the left half for this lookup. We could instead-               -- use both halves and take the median.-               !endElem <- C.read src (start + chunk * (ix + 1))-               !endA <- findIndexOfGtElem src (endElem :: k) prevEndA mid-               !endB <- findIndexOfGtElem src endElem prevEndB end-               forkST_ $ do-                 let !startA = prevEndA-                     !startB = prevEndB-                     !startDst = (startA - start) + (startB - mid) + start-                 mergeNonContiguousTagged src dst srcTags dstTags startA endA startB endB startDst-                 putLock lock-               go endA endB (ix + 1)-  !endElem <- C.read src (start + chunk) -  !endA <- findIndexOfGtElem src (endElem :: k) start mid-  !endB <- findIndexOfGtElem src endElem mid end-  forkST_ $ do-    let !startA = start-        !startB = mid-        !startDst = (startA - start) + (startB - mid) + start-    mergeNonContiguousTagged src dst srcTags dstTags startA endA startB endB startDst-    putLock lock-  total <- go endA endB 1-  replicateM_ total (takeLock lock)-  where-  !chunk = unsafeQuot (end - start) threads- unsafeQuot :: Int -> Int -> Int unsafeQuot (I# a) (I# b) = I# (quotInt# a b)---- If the needle is bigger than everything in the slice--- of the array, this returns the end index (which is out--- of bounds). Callers of this function should be able--- to handle that.-findIndexOfGtElem :: forall arr s a. (Contiguous arr, Element arr a, Ord a)-  => Mutable arr s a -> a -> Int -> Int -> ST s Int-{-# INLINABLE findIndexOfGtElem #-}-findIndexOfGtElem !v !needle !start !end = go start end-  where-  go :: Int -> Int -> ST s Int-  go !lo !hi = if lo < hi-    then do-      let !mid = lo + half (hi - lo)-      !val <- C.read v mid-      if | val == needle -> gallopToGtIndex v needle (mid + 1) hi-         | val < needle -> go (mid + 1) hi-         | otherwise -> go lo mid-    else return lo---- | TODO: should probably turn this into a real galloping search-gallopToGtIndex :: forall arr s a. (Contiguous arr, Element arr a, Ord a)-  => Mutable arr s a -> a -> Int -> Int -> ST s Int-{-# INLINABLE gallopToGtIndex #-}-gallopToGtIndex !v !val !start !end = go start-  where-  go :: Int -> ST s Int-  go !ix = if ix < end-    then do-      !a <- C.read v ix-      if a > val-        then return ix-        else go (ix + 1)-    else return end+{-# INLINE unsafeQuot #-}  -- stepA assumes that we previously incremented ixA. -- Consequently, we do not need to check that ixB@@ -583,7 +332,7 @@   -> Int -- end B   -> Int -- start destination   -> ST s ()-{-# INLINABLE mergeNonContiguous #-}+{-# INLINE mergeNonContiguous #-} mergeNonContiguous !src !dst !startA !endA !startB !endB !startDst =   if startB < endB     then stepA startA startB startDst@@ -611,9 +360,9 @@     then continue ixA ixB ixDst     else finishB ixB ixDst   finishB :: Int -> Int -> ST s ()-  finishB !ixB !ixDst = C.copyMutable dst ixDst src ixB (endB - ixB)+  finishB !ixB !ixDst = C.copyMut dst ixDst (C.sliceMut src ixB (endB - ixB))   finishA :: Int -> Int -> ST s ()-  finishA !ixA !ixDst = C.copyMutable dst ixDst src ixA (endA - ixA)+  finishA !ixA !ixDst = C.copyMut dst ixDst (C.sliceMut src ixA (endA - ixA))  mergeNonContiguousTagged :: forall karr varr k v s. (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v)   => Mutable karr s k -- source@@ -626,7 +375,7 @@   -> Int -- end B   -> Int -- start destination   -> ST s ()-{-# INLINABLE mergeNonContiguousTagged #-}+{-# INLINE mergeNonContiguousTagged #-} mergeNonContiguousTagged !src !dst !srcTags !dstTags !startA !endA !startB !endB !startDst =   if startB < endB     then stepA startA startB startDst@@ -657,12 +406,12 @@     else finishB ixB ixDst   finishB :: Int -> Int -> ST s ()   finishB !ixB !ixDst = do-    C.copyMutable dst ixDst src ixB (endB - ixB)-    C.copyMutable dstTags ixDst srcTags ixB (endB - ixB)+    C.copyMut dst ixDst (C.sliceMut src ixB (endB - ixB))+    C.copyMut dstTags ixDst (C.sliceMut srcTags ixB (endB - ixB))   finishA :: Int -> Int -> ST s ()   finishA !ixA !ixDst = do-    C.copyMutable dst ixDst src ixA (endA - ixA)-    C.copyMutable dstTags ixDst srcTags ixA (endA - ixA)+    C.copyMut dst ixDst (C.sliceMut src ixA (endA - ixA))+    C.copyMut dstTags ixDst (C.sliceMut srcTags ixA (endA - ixA))  threshold :: Int threshold = 16@@ -675,7 +424,7 @@   -> Int -- start   -> Int -- end   -> ST s ()-{-# INLINABLE insertionSortRange #-}+{-# INLINE insertionSortRange #-} insertionSortRange !arr !start !end = go start   where   go :: Int -> ST s ()@@ -692,7 +441,7 @@   -> Int   -> Int   -> ST s ()-{-# INLINABLE insertElement #-}+{-# INLINE insertElement #-} insertElement !arr !a !start !end = go end   where   go :: Int -> ST s ()@@ -701,11 +450,11 @@       !b <- C.read arr (ix - 1)       if b <= a         then do-          C.copyMutable arr (ix + 1) arr ix (end - ix)+          C.copyMut arr (ix + 1) (C.sliceMut arr ix (end - ix))           C.write arr ix a         else go (ix - 1)     else do-      C.copyMutable arr (ix + 1) arr ix (end - ix)+      C.copyMut arr (ix + 1) (C.sliceMut arr ix (end - ix))       C.write arr ix a  insertionSortTaggedRange :: forall karr varr s k v. (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v)@@ -714,7 +463,7 @@   -> Int -- start   -> Int -- end   -> ST s ()-{-# INLINABLE insertionSortTaggedRange #-}+{-# INLINE insertionSortTaggedRange #-} insertionSortTaggedRange !karr !varr !start !end = go start   where   go :: Int -> ST s ()@@ -734,7 +483,7 @@   -> Int   -> Int   -> ST s ()-{-# INLINABLE insertElementTagged #-}+{-# INLINE insertElementTagged #-} insertElementTagged !karr !varr !a !v !start !end = go end   where   go :: Int -> ST s ()@@ -743,48 +492,16 @@       !b <- C.read karr (ix - 1)       if b <= a         then do-          C.copyMutable karr (ix + 1) karr ix (end - ix)+          C.copyMut karr (ix + 1) (C.sliceMut karr ix (end - ix))           C.write karr ix a-          C.copyMutable varr (ix + 1) varr ix (end - ix)+          C.copyMut varr (ix + 1) (C.sliceMut varr ix (end - ix))           C.write varr ix v         else go (ix - 1)     else do-      C.copyMutable karr (ix + 1) karr ix (end - ix)+      C.copyMut karr (ix + 1) (C.sliceMut karr ix (end - ix))       C.write karr ix a-      C.copyMutable varr (ix + 1) varr ix (end - ix)+      C.copyMut varr (ix + 1) (C.sliceMut varr ix (end - ix))       C.write varr ix v---forkST_ :: ST s a -> ST s ()-forkST_ action = ST $ \s1 -> case forkST# action s1 of-  (# s2, _ #) -> (# s2, () #)--forkST# :: a -> State# s -> (# State# s, ThreadId# #)-forkST# = unsafeCoerce# fork#--data Lock s = Lock (MVar# s ())--newLock :: ST s (Lock s)-newLock = ST $ \s1 -> case newMVar# s1 of-  (# s2, v #) -> (# s2, Lock v #)--takeLock :: Lock s -> ST s ()-takeLock (Lock mvar#) = ST $ \ s# -> takeMVar# mvar# s#--putLock  :: Lock s -> ST s ()-putLock (Lock mvar#) = ST $ \ s# ->-  case putMVar# mvar# () s# of-    s2# -> (# s2#, () #)---- | Execute the first computation on the main thread and---   the second one on another thread in parallel. Blocks---   until both are finished.-tandem :: ST s () -> ST s () -> ST s ()-tandem a b = do-  lock <- newLock-  forkST_ (b >> putLock lock)-  a-  takeLock lock  -- $setup --