moonlight-delta-0.1.0.0: src-patch/Moonlight/Delta/Patch/Internal/Compose/Range.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
-- Loop-local rebinding (state/cursor/coverage) is the engine idiom here; shadowing is deliberate.
{-# OPTIONS_GHC -Wno-name-shadowing #-}
module Moonlight.Delta.Patch.Internal.Compose.Range
( disjointComposition,
splitPatchRange,
appendPatch,
appendPages,
pageToAscCells,
)
where
import Control.Monad.ST (ST, runST)
import Data.Map.Internal qualified as MapInternal
import Data.Map.Strict qualified as Map
import Moonlight.Delta.Patch.Internal.Builder
import Moonlight.Delta.Patch.Internal.Cell
( cellAfterEndpoint,
cellBeforeEndpoint,
)
import Moonlight.Delta.Patch.Internal.Page
import Moonlight.Delta.Patch.Internal.Types
import Prelude
disjointComposition :: Ord key => Patch key value -> Patch key value -> Maybe (Patch key value)
disjointComposition
(PagedPatch olderCount olderPages)
(PagedPatch newerCount newerPages) =
case (Map.lookupMax olderPages, Map.lookupMin newerPages) of
(Just (olderMaximum, _), Just (newerMaximum, newerFirstPage))
| olderMaximum < pageMinimumKey newerMaximum newerFirstPage ->
Just (PagedPatch (olderCount + newerCount) (MapInternal.link2 olderPages newerPages))
_ ->
case (Map.lookupMax newerPages, Map.lookupMin olderPages) of
(Just (newerMaximum, _), Just (olderMaximum, olderFirstPage))
| newerMaximum < pageMinimumKey olderMaximum olderFirstPage ->
Just (PagedPatch (olderCount + newerCount) (MapInternal.link2 newerPages olderPages))
_ -> Nothing
disjointComposition _ _ =
Nothing
{-# INLINABLE disjointComposition #-}
splitPatchRange ::
(PatchKey key, PatchValue value) =>
key ->
key ->
Patch key value ->
(Patch key value, Patch key value, Patch key value)
splitPatchRange minimumKey maximumKey patch =
let (!beforePages, !atOrAfterMinimum) =
splitPagesLessThan minimumKey (pagesOf patch)
(!middlePages, !afterPages) =
splitPagesLessOrEqual maximumKey atOrAfterMinimum
in (patchFromPages beforePages, patchFromPages middlePages, patchFromPages afterPages)
{-# INLINABLE splitPatchRange #-}
patchFromPages :: Map.Map key (Page key value) -> Patch key value
patchFromPages pages =
PagedPatch (pageMapEntryCount pages) pages
{-# INLINE patchFromPages #-}
pageMapEntryCount :: Map.Map key (Page key value) -> Int
pageMapEntryCount =
Map.foldlWithKey' (\count _ page -> count + pageCount page) 0
{-# INLINE pageMapEntryCount #-}
appendPatch :: Patch key value -> Patch key value -> Patch key value
appendPatch left right =
PagedPatch
(entryCount left + entryCount right)
(appendPages (pagesOf left) (pagesOf right))
{-# INLINE appendPatch #-}
appendPages :: Map.Map key value -> Map.Map key value -> Map.Map key value
appendPages left right =
case (left, right) of
(MapInternal.Tip, _) ->
right
(_, MapInternal.Tip) ->
left
_ ->
MapInternal.link2 left right
{-# INLINE appendPages #-}
splitPagesLessThan ::
(PatchKey key, PatchValue value) =>
key ->
Map.Map key (Page key value) ->
(Map.Map key (Page key value), Map.Map key (Page key value))
splitPagesLessThan target pages =
case pageForKey target pages of
Nothing ->
(pages, Map.empty)
Just (maximumKey, page) ->
let (!beforePages, !_found, !afterPages) =
Map.splitLookup maximumKey pages
!minimumKey =
pageMinimumKey maximumKey page
in if target <= minimumKey
then (beforePages, appendPages (Map.singleton maximumKey page) afterPages)
else
let !splitIndex =
pageLowerBound target maximumKey page
!beforeSlice =
pageSlicePages maximumKey page 0 splitIndex
!afterSlice =
pageSlicePages maximumKey page splitIndex (pageCount page - splitIndex)
in ( appendPages beforePages beforeSlice,
appendPages afterSlice afterPages
)
{-# INLINABLE splitPagesLessThan #-}
splitPagesLessOrEqual ::
(PatchKey key, PatchValue value) =>
key ->
Map.Map key (Page key value) ->
(Map.Map key (Page key value), Map.Map key (Page key value))
splitPagesLessOrEqual target pages =
case pageForKey target pages of
Nothing ->
(pages, Map.empty)
Just (maximumKey, page) ->
let (!beforePages, !_found, !afterPages) =
Map.splitLookup maximumKey pages
!minimumKey =
pageMinimumKey maximumKey page
in if target < minimumKey
then (beforePages, appendPages (Map.singleton maximumKey page) afterPages)
else
if maximumKey <= target
then (appendPages beforePages (Map.singleton maximumKey page), afterPages)
else
let !splitIndex =
pageUpperBound target maximumKey page
!beforeSlice =
pageSlicePages maximumKey page 0 splitIndex
!afterSlice =
pageSlicePages maximumKey page splitIndex (pageCount page - splitIndex)
in ( appendPages beforePages beforeSlice,
appendPages afterSlice afterPages
)
{-# INLINABLE splitPagesLessOrEqual #-}
pageLowerBound :: Ord key => key -> key -> Page key value -> Int
pageLowerBound target maximumKey page =
search 0 (pageCount page)
where
search !low !high
| low == high =
low
| otherwise =
let !middle = (low + high) `quot` 2
!middleKey = pageKeyAt maximumKey page middle
in case compare middleKey target of
LT -> search (middle + 1) high
EQ -> middle
GT -> search low middle
{-# INLINABLE pageLowerBound #-}
pageUpperBound :: Ord key => key -> key -> Page key value -> Int
pageUpperBound target maximumKey page =
search 0 (pageCount page)
where
search !low !high
| low == high =
low
| otherwise =
let !middle = (low + high) `quot` 2
!middleKey = pageKeyAt maximumKey page middle
in case compare middleKey target of
GT -> search low middle
_ -> search (middle + 1) high
{-# INLINABLE pageUpperBound #-}
pageSlicePages ::
forall key value.
(PatchKey key, PatchValue value) =>
key ->
Page key value ->
Int ->
Int ->
Map.Map key (Page key value)
pageSlicePages maximumKey page !start !sliceLength
| sliceLength <= 0 =
Map.empty
| start <= 0 && sliceLength == pageCount page =
Map.singleton maximumKey page
| otherwise =
pagesOf $
runST $ do
builder <- newBuilder
appendSliceRows builder 0
finishBuilder builder
where
appendSliceRows :: Builder s key value -> Int -> ST s ()
appendSliceRows builder !offset
| offset == sliceLength =
pure ()
| otherwise = do
let !rowIndex = start + offset
!cell = rowCellAt page rowIndex
appendTransition
builder
(pageKeyAt maximumKey page rowIndex)
(cellBeforeEndpoint cell)
(cellAfterEndpoint cell)
appendSliceRows builder (offset + 1)
{-# INLINABLE pageSlicePages #-}
pageToAscCells :: key -> Page key value -> [(key, CellPatch value)]
pageToAscCells maximumKey page =
fmap
(\index -> (pageKeyAt maximumKey page index, rowCellAt page index))
[0 .. pageCount page - 1]
{-# INLINE pageToAscCells #-}