packages feed

radix-tree-1.1.0.0: src/Data/Radix1Tree/Word8/Strict/Zipper.hs

{-# LANGUAGE BangPatterns #-}

{-| Spine-strict radix tree location manipulation.

    Allows lookup and successive insertion without retaining the entirety
    of the key in memory.
 -}

module Data.Radix1Tree.Word8.Strict.Zipper
  ( Context1
  , descend
  , focus
  ) where

import           Data.ByteArray.NonEmpty
import           Data.RadixNTree.Word8.Key
import           Data.RadixNTree.Word8.Strict
import           Radix.Word8.Foundation

import           Data.Primitive.ByteArray



data Past a = Leftward
                !(Past a)
                {-# UNPACK #-} !Prefix
                !(Radix1Tree a)

            | Rightward
                !(Past a)
                {-# UNPACK #-} !Prefix
                !(Radix1Tree a)

            | Downward
                !(Past a)
                {-# UNPACK #-} !ByteArray
                {-# UNPACK #-} !(Maybe a)

            | Top


-- | A location inside the radix tree.
data Context1 a = -- | Corresponds to a 'Tip'.
                  Context1
                    !(Past a)
                    {-# UNPACK #-} !Int       -- ^ Next index in the byte array.
                    {-# UNPACK #-} !ByteArray
                    {-# UNPACK #-} !(Maybe a)
                    !(Radix1Tree a)



{-# INLINE descend #-}
-- | \(\mathcal{O}(\min(x,k))\).
--   Move down the tree by the extent of the given key.
--   Returns 'Nothing' if the resulting position is outside of the tree.
--
--   @since 1.1
descend :: Feed1 -> Either (Radix1Tree a) (Context1 a) -> Maybe (Context1 a)
descend (Feed1 w0 feed) =
  feed $ \step ->

    let go !past !w !s t =
          case t of
            Bin p l r ->
              if w < p
                then go (Leftward  past p r) w s l
                else go (Rightward past p l) w s r

            Tip arr mx dx -> goarr past arr mx dx w s 0

            Nil -> Nothing

        goarr !past !arr !mx dx = goarr_
          where
            goarr_ v !z n
              | v == indexByteArray arr n =
                  let n' = n + 1
                  in case step z of
                       More u z' -> if n' >= sizeofByteArray arr
                                      then go (Downward past arr mx) u z' dx
                                      else goarr_ u z' n'

                       Done      -> Just $! Context1 past n' arr mx dx

              | otherwise = Nothing

    in \s0 ei ->
         case ei of
           Left r                           -> go Top w0 s0 r
           Right (Context1 past n arr mx dx) ->
             if n == sizeofByteArray arr
               then go (Downward past arr mx) w0 s0 dx
               else goarr past arr mx dx w0 s0 n



-- | \(\mathcal{O}(1)\).
--   Retrieve the value at the current position, if any exists,
--   together with the insertion function for the current position.
--
--   @since 1.1
focus :: Context1 a -> Maybe (a, a -> Radix1Tree a)
focus (Context1 past n arr mx dx)
  | n == sizeofByteArray arr, Just x <- mx =
      Just $! (x, \y -> rebuild (Tip arr (Just y) dx) past)

  | otherwise = Nothing



rebuild :: Radix1Tree a -> Past a -> Radix1Tree a
rebuild !x past =
  case past of
    Leftward  past' p r   -> rebuild (Bin p x r) past'
    Rightward past' p l   -> rebuild (Bin p l x) past'
    Downward past' brr my -> rebuild (Tip brr my x) past'
    Top                   -> x