packages feed

radix-tree 1.0.0.2 → 1.1.0.0

raw patch · 22 files changed

+676/−410 lines, 22 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.Radix1Tree.Word8.Lazy: Inside :: Location
- Data.Radix1Tree.Word8.Lazy: Outside :: Location
- Data.Radix1Tree.Word8.Lazy: cursor :: Radix1Tree a -> Cursor a
- Data.Radix1Tree.Word8.Lazy: data Cursor a
- Data.Radix1Tree.Word8.Lazy: data Location
- Data.Radix1Tree.Word8.Lazy: locate :: Cursor a -> Location
- Data.Radix1Tree.Word8.Lazy: move :: Feed1 -> Cursor a -> Cursor a
- Data.Radix1Tree.Word8.Lazy: stop :: Cursor a -> Maybe a
- Data.Radix1Tree.Word8.Strict: Inside :: Location
- Data.Radix1Tree.Word8.Strict: Outside :: Location
- Data.Radix1Tree.Word8.Strict: cursor :: Radix1Tree a -> Cursor a
- Data.Radix1Tree.Word8.Strict: data Cursor a
- Data.Radix1Tree.Word8.Strict: data Location
- Data.Radix1Tree.Word8.Strict: locate :: Cursor a -> Location
- Data.Radix1Tree.Word8.Strict: move :: Feed1 -> Cursor a -> Cursor a
- Data.Radix1Tree.Word8.Strict: stop :: Cursor a -> Maybe a
- Data.RadixTree.Word8.Lazy: Inside :: Location
- Data.RadixTree.Word8.Lazy: Outside :: Location
- Data.RadixTree.Word8.Lazy: cursor :: RadixTree a -> Cursor a
- Data.RadixTree.Word8.Lazy: data Cursor a
- Data.RadixTree.Word8.Lazy: data Location
- Data.RadixTree.Word8.Lazy: locate :: Cursor a -> Location
- Data.RadixTree.Word8.Lazy: move :: Feed -> Cursor a -> Cursor a
- Data.RadixTree.Word8.Lazy: stop :: Cursor a -> Maybe a
- Data.RadixTree.Word8.Strict: Inside :: Location
- Data.RadixTree.Word8.Strict: Outside :: Location
- Data.RadixTree.Word8.Strict: cursor :: RadixTree a -> Cursor a
- Data.RadixTree.Word8.Strict: data Cursor a
- Data.RadixTree.Word8.Strict: data Location
- Data.RadixTree.Word8.Strict: locate :: Cursor a -> Location
- Data.RadixTree.Word8.Strict: move :: Feed -> Cursor a -> Cursor a
- Data.RadixTree.Word8.Strict: stop :: Cursor a -> Maybe a
+ Data.Radix1Tree.Word8.Lazy.Zipper: data Context1 a
+ Data.Radix1Tree.Word8.Lazy.Zipper: descend :: Feed1 -> Either (Radix1Tree a) (Context1 a) -> Maybe (Context1 a)
+ Data.Radix1Tree.Word8.Lazy.Zipper: focus :: Context1 a -> Maybe (a, a -> Radix1Tree a)
+ Data.Radix1Tree.Word8.Strict.Pointer: data Pointer
+ Data.Radix1Tree.Word8.Strict.Pointer: follow :: a -> Pointer -> Radix1Tree a -> a
+ Data.Radix1Tree.Word8.Strict.Pointer: pointer :: Feed1 -> Radix1Tree a -> Maybe Pointer
+ Data.Radix1Tree.Word8.Strict.Zipper: data Context1 a
+ Data.Radix1Tree.Word8.Strict.Zipper: descend :: Feed1 -> Either (Radix1Tree a) (Context1 a) -> Maybe (Context1 a)
+ Data.Radix1Tree.Word8.Strict.Zipper: focus :: Context1 a -> Maybe (a, a -> Radix1Tree a)
+ Data.RadixTree.Word8.Strict.Pointer: data Pointer
+ Data.RadixTree.Word8.Strict.Pointer: follow :: a -> Pointer -> RadixTree a -> a
+ Data.RadixTree.Word8.Strict.Pointer: pointer :: Feed -> RadixTree a -> Maybe Pointer
- Data.Radix1Tree.Word8.Lazy.Unsafe: mask :: Key -> Mask -> Word
+ Data.Radix1Tree.Word8.Lazy.Unsafe: mask :: Key -> Mask -> Prefix
- Data.Radix1Tree.Word8.Lazy.Unsafe: type Key = Word
+ Data.Radix1Tree.Word8.Lazy.Unsafe: type Key = Word8
- Data.Radix1Tree.Word8.Lazy.Unsafe: type Mask = Word
+ Data.Radix1Tree.Word8.Lazy.Unsafe: type Mask = Word8
- Data.Radix1Tree.Word8.Lazy.Unsafe: type Prefix = Word
+ Data.Radix1Tree.Word8.Lazy.Unsafe: type Prefix = Word8
- Data.Radix1Tree.Word8.Strict.Unsafe: mask :: Key -> Mask -> Word
+ Data.Radix1Tree.Word8.Strict.Unsafe: mask :: Key -> Mask -> Prefix
- Data.Radix1Tree.Word8.Strict.Unsafe: type Key = Word
+ Data.Radix1Tree.Word8.Strict.Unsafe: type Key = Word8
- Data.Radix1Tree.Word8.Strict.Unsafe: type Mask = Word
+ Data.Radix1Tree.Word8.Strict.Unsafe: type Mask = Word8
- Data.Radix1Tree.Word8.Strict.Unsafe: type Prefix = Word
+ Data.Radix1Tree.Word8.Strict.Unsafe: type Prefix = Word8

Files

CHANGELOG.md view
@@ -1,4 +1,13 @@-## 1.0.0.0 -- September 2024+## 1.1.0.0 -- October 2024++* Added `Zipper` modules for non-empty radix trees;++* Removed `Cursor`s from radix tree modules.+  Their functionality is subsumed by the appropriate `Zipper` modules.++* Added `Pointer` modules for strict radix trees;++## 1.0.0.2 -- September 2024  * Fixed `Data.Zebra.Word.fillRange`.   Previously it produced malformed trees in certain cases.
radix-tree.cabal view
@@ -1,5 +1,5 @@ name:          radix-tree-version:       1.0.0.2+version:       1.1.0.0  category:      Data Structures synopsis:      Radix trees@@ -45,6 +45,7 @@                     Data.RadixTree.Word8.Lazy.Unsafe                     Data.RadixTree.Word8.Strict                     Data.RadixTree.Word8.Strict.Debug+                    Data.RadixTree.Word8.Strict.Pointer                     Data.RadixTree.Word8.Strict.TH                     Data.RadixTree.Word8.Strict.Unsafe @@ -54,10 +55,13 @@                     Data.Radix1Tree.Word8.Lazy.Debug                     Data.Radix1Tree.Word8.Lazy.TH                     Data.Radix1Tree.Word8.Lazy.Unsafe+                    Data.Radix1Tree.Word8.Lazy.Zipper                     Data.Radix1Tree.Word8.Strict                     Data.Radix1Tree.Word8.Strict.Debug+                    Data.Radix1Tree.Word8.Strict.Pointer                     Data.Radix1Tree.Word8.Strict.TH                     Data.Radix1Tree.Word8.Strict.Unsafe+                    Data.Radix1Tree.Word8.Strict.Zipper                      Data.Zebra.Word                     Data.Zebra.Word.Debug@@ -80,6 +84,7 @@                     Data.RadixNTree.Word8.Lazy.TH                     Data.RadixNTree.Word8.Strict                     Data.RadixNTree.Word8.Strict.Debug+                    Data.RadixNTree.Word8.Strict.Pointer                     Data.RadixNTree.Word8.Strict.TH                      Data.Zebra.Word.Internal@@ -88,7 +93,6 @@                      Radix.Common                     Radix.Exception-                    Radix.Word8.Common                     Radix.Word8.Debug                     Radix.Word8.Foundation                     Radix.Word.Common
src/Data/Patricia/Word/Lazy/Internal.hs view
@@ -2348,7 +2348,7 @@ -- | \(\mathcal{O}(\min(n,W))\). --   Look up a value at the leftmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeLookupMin :: Patricia a -> (# a #) unsafeLookupMin t =   case t of@@ -2366,7 +2366,7 @@ -- | \(\mathcal{O}(\min(n,W))\). --   Look up a value at the leftmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeLookupMinWithKey :: Patricia a -> Lookup a unsafeLookupMinWithKey t =   case t of@@ -2386,7 +2386,7 @@ -- | \(\mathcal{O}(\min(n,W))\). --   Look up a value at the rightmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeLookupMax :: Patricia a -> (# a #) unsafeLookupMax t =   case t of@@ -2404,7 +2404,7 @@ -- | \(\mathcal{O}(\min(n,W))\). --   Look up a value at the rightmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeLookupMaxWithKey :: Patricia a -> Lookup a unsafeLookupMaxWithKey t =   case t of@@ -2543,7 +2543,7 @@ -- | \(\mathcal{O}(\min(n,W))\). --   Look up the leftmost value and return it alongside the tree without it. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeMinView :: Patricia a -> ViewL a unsafeMinView t =   case t of@@ -2570,7 +2570,7 @@ -- | \(\mathcal{O}(\min(n,W))\). --   Look up the rightmost value and return it alongside the tree without it. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeMaxView :: Patricia a -> ViewR a unsafeMaxView t =   case t of
src/Data/Patricia/Word/Strict/Internal.hs view
@@ -2755,7 +2755,7 @@ -- | \(\mathcal{O}(\min(n,W))\). --   Look up a value at the leftmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeLookupMin :: Patricia a -> (# a #) unsafeLookupMin t =   case t of@@ -2773,7 +2773,7 @@ -- | \(\mathcal{O}(\min(n,W))\). --   Look up a value at the leftmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeLookupMinWithKey :: Patricia a -> Lookup a unsafeLookupMinWithKey t =   case t of@@ -2793,7 +2793,7 @@ -- | \(\mathcal{O}(\min(n,W))\). --   Look up a value at the rightmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeLookupMax :: Patricia a -> (# a #) unsafeLookupMax t =   case t of@@ -2811,7 +2811,7 @@ -- | \(\mathcal{O}(\min(n,W))\). --   Look up a value at the rightmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeLookupMaxWithKey :: Patricia a -> Lookup a unsafeLookupMaxWithKey t =   case t of@@ -3011,7 +3011,7 @@ -- | \(\mathcal{O}(\min(n,W))\). --   Look up the leftmost value and return it alongside the tree without it. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeMinView :: Patricia a -> ViewL a unsafeMinView t =   case t of@@ -3038,7 +3038,7 @@ -- | \(\mathcal{O}(\min(n,W))\). --   Look up the rightmost value and return it alongside the tree without it. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeMaxView :: Patricia a -> ViewR a unsafeMaxView t =   case t of
src/Data/Radix1Tree/Word8/Lazy.hs view
@@ -69,21 +69,6 @@   , Data.Radix1Tree.Word8.Lazy.member   , subtree -    -- *** Chunked-    ---    -- | Chunked lookup allows providing the key piece by piece while retaining-    --   the ability to check for early failure.-    ---    --   Note that while 'subtree' can be used to achieve the same result,-    --   it is more expensive allocation-wise, as it must ensure that-    --   the resulting tree is well-formed after each chunk application.-  , Cursor-  , cursor-  , move-  , stop-  , Location (..)-  , locate-     -- ** Insert   , insert   , insertWith@@ -455,18 +440,6 @@ --   Prefix the root of the tree with the given key. prefix :: Feed1 -> RadixTree a -> Radix1Tree a prefix = prefix1----- | \(\mathcal{O}(1)\).---   Make a cursor that points to the root of the tree.-cursor :: Radix1Tree a -> Cursor a-cursor = cursor1--{-# INLINE move #-}--- | \(\mathcal{O}(\min(x,k))\).---   Move the cursor down by the extent of the given key.-move :: Feed1 -> Cursor a -> Cursor a-move = move1   
src/Data/Radix1Tree/Word8/Lazy/Unsafe.hs view
@@ -79,63 +79,63 @@ import           Data.RadixNTree.Word8.Common import           Data.RadixNTree.Word8.Lazy import           Radix.Exception-import           Radix.Word.Foundation+import           Radix.Word8.Foundation    -- | \(\mathcal{O}(k)\). --   Look up a value at the leftmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeLookupMin :: Radix1Tree a -> (# a #) unsafeLookupMin = unsafeLookupMin1  -- | \(\mathcal{O}(k)\). --   Look up a value at the leftmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeLookupMinWithKey :: Radix1Tree a -> Lookup1 a unsafeLookupMinWithKey = unsafeLookupMinWithKey1  -- | \(\mathcal{O}(1)\texttt{+}, \mathcal{O}(k)\). --   Delete a value at the leftmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeDeleteMin :: Radix1Tree a -> Radix1Tree a unsafeDeleteMin = unsafeDeleteMin1  -- | \(\mathcal{O}(1)\texttt{+}, \mathcal{O}(k)\). --   Update a value at the leftmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeAdjustMin :: (a -> a) -> Radix1Tree a -> Radix1Tree a unsafeAdjustMin = unsafeAdjustMin1  -- | \(\mathcal{O}(1)\texttt{+}, \mathcal{O}(k)\). --   Update a value at the leftmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeAdjustMinWithKey :: (Build1 -> a -> a) -> Radix1Tree a -> Radix1Tree a unsafeAdjustMinWithKey = unsafeAdjustMinWithKey1  -- | \(\mathcal{O}(1)\texttt{+}, \mathcal{O}(k)\). --   Update or delete a value at the leftmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeUpdateMin :: (a -> Maybe a) -> Radix1Tree a -> Radix1Tree a unsafeUpdateMin = unsafeUpdateMin1  -- | \(\mathcal{O}(1)\texttt{+}, \mathcal{O}(k)\). --   Update or delete a value at the leftmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeUpdateMinWithKey :: (Build1 -> a -> Maybe a) -> Radix1Tree a -> Radix1Tree a unsafeUpdateMinWithKey = unsafeUpdateMinWithKey1  -- | \(\mathcal{O}(\min(x,k))\). --   Look up the leftmost value and return it alongside the tree without it. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeMinView :: Radix1Tree a -> ViewL1 a unsafeMinView = unsafeMinView1 @@ -144,56 +144,56 @@ -- | \(\mathcal{O}(k)\). --   Look up a value at the rightmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeLookupMax :: Radix1Tree a -> (# a #) unsafeLookupMax = unsafeLookupMax1  -- | \(\mathcal{O}(k)\). --   Look up a value at the rightmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeLookupMaxWithKey :: Radix1Tree a -> Lookup1 a unsafeLookupMaxWithKey = unsafeLookupMaxWithKey1  -- | \(\mathcal{O}(1)\texttt{+}, \mathcal{O}(k)\). --   Delete a value at the rightmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeDeleteMax :: Radix1Tree a -> Radix1Tree a unsafeDeleteMax = unsafeDeleteMax1  -- | \(\mathcal{O}(1)\texttt{+}, \mathcal{O}(k)\). --   Update a value at the rightmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeAdjustMax :: (a -> a) -> Radix1Tree a -> Radix1Tree a unsafeAdjustMax = unsafeAdjustMax1  -- | \(\mathcal{O}(1)\texttt{+}, \mathcal{O}(k)\). --   Update a value at the rightmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeAdjustMaxWithKey :: (Build1 -> a -> a) -> Radix1Tree a -> Radix1Tree a unsafeAdjustMaxWithKey = unsafeAdjustMaxWithKey1  -- | \(\mathcal{O}(1)\texttt{+}, \mathcal{O}(k)\). --   Update or delete a value at the rightmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeUpdateMax :: (a -> Maybe a) -> Radix1Tree a -> Radix1Tree a unsafeUpdateMax = unsafeUpdateMax1  -- | \(\mathcal{O}(1)\texttt{+}, \mathcal{O}(k)\). --   Update or delete a value at the rightmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeUpdateMaxWithKey :: (Build1 -> a -> Maybe a) -> Radix1Tree a -> Radix1Tree a unsafeUpdateMaxWithKey = unsafeUpdateMaxWithKey1  -- | \(\mathcal{O}(\min(x,k))\). --   Look up the rightmost value and return it alongside the tree without it. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeMaxView :: Radix1Tree a -> ViewR1 a unsafeMaxView = unsafeMaxView1 
+ src/Data/Radix1Tree/Word8/Lazy/Zipper.hs view
@@ -0,0 +1,118 @@+{-# LANGUAGE BangPatterns #-}++{-| Spine-lazy radix tree location manipulation.++    Allows lookup and successive insertion without retaining the entirety+    of the key in memory.+ -}++module Data.Radix1Tree.Word8.Lazy.Zipper+  ( Context1+  , descend+  , focus+  ) where++import           Data.ByteArray.NonEmpty+import           Data.RadixNTree.Word8.Key+import           Data.RadixNTree.Word8.Lazy+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
src/Data/Radix1Tree/Word8/Strict.hs view
@@ -68,21 +68,6 @@   , Data.Radix1Tree.Word8.Strict.member   , subtree -    -- *** Chunked-    ---    -- | Chunked lookup allows providing the key piece by piece while retaining-    --   the ability to check for early failure.-    ---    --   Note that while 'subtree' can be used to achieve the same result,-    --   it is more expensive allocation-wise, as it must ensure that-    --   the resulting tree is well-formed after each chunk application.-  , Cursor-  , cursor-  , move-  , stop-  , Location (..)-  , locate-     -- ** Insert   , insert   , insertWith@@ -488,18 +473,6 @@ --   Prefix the root of the tree with the given key. prefix :: Feed1 -> RadixTree a -> Radix1Tree a prefix = prefix1----- | \(\mathcal{O}(1)\).---   Make a cursor that points to the root of the tree.-cursor :: Radix1Tree a -> Cursor a-cursor = cursor1--{-# INLINE move #-}--- | \(\mathcal{O}(\min(x,k))\).---   Move the cursor down by the extent of the given key.-move :: Feed1 -> Cursor a -> Cursor a-move = move1   
+ src/Data/Radix1Tree/Word8/Strict/Pointer.hs view
@@ -0,0 +1,38 @@+{-| Compressed references for spine-strict radix trees.++    Pointers have a much smaller memory footprint and+    allow faster lookup in trees that are known to never change shape.+ -}++module Data.Radix1Tree.Word8.Strict.Pointer+  ( Pointer+  , pointer+  , follow+  ) where++import           Data.RadixNTree.Word8.Key (Feed1)+import           Data.RadixNTree.Word8.Strict (Radix1Tree)+import           Data.RadixNTree.Word8.Strict.Pointer++++{-# INLINE pointer #-}+-- | \(\mathcal{O}(\min(x,k))\).+--   Create a pointer that mirrors an existing key.+--+--   The pointer is only guaranteed to behave correctly for any tree that holds the+--   same set of keys as the provided one.+--+--   @since 1.1+pointer :: Feed1 -> Radix1Tree a -> Maybe Pointer+pointer = pointer1++++-- | \(\mathcal{O}(\log n)\).+--   Look up the value at a pointer in the tree, falling back to the given default value+--   if it does not exist.+--+--   @since 1.1+follow :: a -> Pointer -> Radix1Tree a -> a+follow = follow1
src/Data/Radix1Tree/Word8/Strict/Unsafe.hs view
@@ -83,42 +83,42 @@ import           Data.RadixNTree.Word8.Common import           Data.RadixNTree.Word8.Strict import           Radix.Exception-import           Radix.Word.Foundation+import           Radix.Word8.Foundation    -- | \(\mathcal{O}(k)\). --   Look up a value at the leftmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeLookupMin :: Radix1Tree a -> (# a #) unsafeLookupMin = unsafeLookupMin1  -- | \(\mathcal{O}(k)\). --   Look up a value at the leftmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeLookupMinWithKey :: Radix1Tree a -> Lookup1 a unsafeLookupMinWithKey = unsafeLookupMinWithKey1  -- | \(\mathcal{O}(k)\). --   Delete a value at the leftmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeDeleteMin :: Radix1Tree a -> Radix1Tree a unsafeDeleteMin = unsafeDeleteMin1  -- | \(\mathcal{O}(k)\). --   Update a value at the leftmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeAdjustMin :: (a -> a) -> Radix1Tree a -> Radix1Tree a unsafeAdjustMin = unsafeAdjustMin1  -- | \(\mathcal{O}(k)\). --   Update a value at the leftmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeAdjustMinWithKey :: (Build1 -> a -> a) -> Radix1Tree a -> Radix1Tree a unsafeAdjustMinWithKey = unsafeAdjustMinWithKey1 @@ -127,7 +127,7 @@ -- --   New value is evaluated to WHNF. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeAdjustMin' :: (a -> a) -> Radix1Tree a -> Radix1Tree a unsafeAdjustMin' = unsafeAdjustMin1' @@ -136,7 +136,7 @@ -- --   New value is evaluated to WHNF. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeAdjustMinWithKey' :: (Build1 -> a -> a) -> Radix1Tree a -> Radix1Tree a unsafeAdjustMinWithKey' = unsafeAdjustMinWithKey1' @@ -153,7 +153,7 @@ -- | \(\mathcal{O}(\min(x,k))\). --   Look up the leftmost value and return it alongside the tree without it. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeMinView :: Radix1Tree a -> ViewL1 a unsafeMinView = unsafeMinView1 @@ -162,35 +162,35 @@ -- | \(\mathcal{O}(k)\). --   Look up a value at the rightmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeLookupMax :: Radix1Tree a -> (# a #) unsafeLookupMax = unsafeLookupMax1  -- | \(\mathcal{O}(k)\). --   Look up a value at the rightmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeLookupMaxWithKey :: Radix1Tree a -> Lookup1 a unsafeLookupMaxWithKey = unsafeLookupMaxWithKey1  -- | \(\mathcal{O}(k)\). --   Delete a value at the rightmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeDeleteMax :: Radix1Tree a -> Radix1Tree a unsafeDeleteMax = unsafeDeleteMax1  -- | \(\mathcal{O}(k)\). --   Update a value at the rightmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeAdjustMax :: (a -> a) -> Radix1Tree a -> Radix1Tree a unsafeAdjustMax = unsafeAdjustMax1  -- | \(\mathcal{O}(k)\). --   Update a value at the rightmost key in the tree. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeAdjustMaxWithKey :: (Build1 -> a -> a) -> Radix1Tree a -> Radix1Tree a unsafeAdjustMaxWithKey = unsafeAdjustMaxWithKey1 @@ -199,7 +199,7 @@ -- --   New value is evaluated to WHNF. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeAdjustMax' :: (a -> a) -> Radix1Tree a -> Radix1Tree a unsafeAdjustMax' = unsafeAdjustMax1' @@ -208,7 +208,7 @@ -- --   New value is evaluated to WHNF. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeAdjustMaxWithKey' :: (Build1 -> a -> a) -> Radix1Tree a -> Radix1Tree a unsafeAdjustMaxWithKey' = unsafeAdjustMaxWithKey1' @@ -225,7 +225,7 @@ -- | \(\mathcal{O}(\min(x,k))\). --   Look up the rightmost value and return it alongside the tree without it. -----   Throws 'MalformedTree' if the tree is empty.+--   Throws t'MalformedTree' if the tree is empty. unsafeMaxView :: Radix1Tree a -> ViewR1 a unsafeMaxView = unsafeMaxView1 
+ src/Data/Radix1Tree/Word8/Strict/Zipper.hs view
@@ -0,0 +1,118 @@+{-# 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
src/Data/RadixNTree/Word8/Key.hs view
@@ -320,9 +320,11 @@   -data CarryBS = CarryBS Int Strict.ByteString Lazy.ByteString+data CarryBS = CarryBS+                 {-# UNPACK #-} !Int+                 !Strict.ByteString+                 !Lazy.ByteString -{-# INLINE stepLazyByteString #-} stepLazyByteString :: CarryBS -> Step Word8 CarryBS stepLazyByteString (CarryBS n bs lbs) =   if n >= BS.length bs@@ -348,9 +350,12 @@   -data CarryTxt = CarryTxt Int Int ByteArray Lazy.Text+data CarryTxt = CarryTxt+                  {-# UNPACK #-} !Int+                  {-# UNPACK #-} !Int+                  !ByteArray+                  !Lazy.Text -{-# INLINE stepLazyText #-} stepLazyText :: CarryTxt -> Step Word8 CarryTxt stepLazyText (CarryTxt n len arr t) =   if n >= len
src/Data/RadixNTree/Word8/Lazy.hs view
@@ -73,19 +73,6 @@   , subtree1   , prefix1 -  , Point (..)-  , Cursor (..)-  , stop--  , Location (..)-  , locate--  , cursor0-  , move0--  , cursor1-  , move1-   , lookupL0   , lookupL1 @@ -292,7 +279,6 @@ import           Data.RadixNTree.Word8.Key import           Radix.Common import           Radix.Exception-import           Radix.Word8.Common import           Radix.Word8.Foundation  import           Control.Applicative@@ -1133,98 +1119,6 @@         Nil           -> Nil      Just _  -> Tip (fromStep step w z) mx t------ | Current position in the tree.-data Point = -- | Above a node.-             Seam--             -- | In the middle of a 'Tip'.-           | Plane-               {-# UNPACK #-} !Int       -- ^ Always greater than @0@ and smaller than-                                         --   the length of the 'ByteArray'.-               {-# UNPACK #-} !ByteArray---- | A particular point in the tree.-data Cursor a = -- | This is effectively a 'Tip' where the 'ByteArray' is optional.-                Cursor-                  {-# UNPACK #-} !Point-                  {-# UNPACK #-} !(Maybe a)-                  (Radix1Tree a)--instance Show a => Show (Cursor a) where-  showsPrec d c =-    showParen (d > 10) $-      showString "Cursor " . showsPrec 11 (stop c)--cursor0 :: RadixTree a -> Cursor a-cursor0 (RadixTree mx t) = Cursor Seam mx t--cursor1 :: Radix1Tree a -> Cursor a-cursor1 = Cursor Seam Nothing--{-# INLINE move0 #-}-move0 :: Feed -> Cursor a -> Cursor a-move0 (Feed feed) = \c ->-  feed $ \step s ->-    case step s of-      More w z -> move_ step w z c-      Done     -> c--{-# INLINE move1 #-}-move1 :: Feed1 -> Cursor a -> Cursor a-move1 (Feed1 w feed) = feed $ \step -> move_ step w--{-# INLINE move_ #-}-move_ :: (x -> Step Word8 x) -> Word8 -> x -> Cursor a -> Cursor a-move_ step = \w s (Cursor point mx dx) ->-  case point of-    Seam        -> go w s dx-    Plane i arr -> goarr arr mx dx w s i-  where-    go !w !s t =-      case t of-        Bin p l r     -> go w s $ if w < p-                                    then l-                                    else r--        Tip brr my dy -> goarr brr my dy w s 0--        Nil           -> Cursor Seam Nothing Nil--    goarr arr mx dx = goarr_-      where-        goarr_ w !s n-          | w == indexByteArray arr n =-              let !n' = n + 1-              in case step s of-                   More v z-                     | n' >= sizeofByteArray arr -> go v z dx-                     | otherwise                 -> goarr_ v z n'--                   Done      ->-                     let !point'-                           | n' >= sizeofByteArray arr = Seam-                           | otherwise                 = Plane n' arr--                     in Cursor point' mx dx--          | otherwise = Cursor Seam Nothing Nil---- | \(\mathcal{O}(1)\).---   Retrieve the value at which the cursor points.-stop :: Cursor a -> Maybe a-stop (Cursor point mx _) =-  case point of-    Seam -> mx-    _    -> Nothing---- | \(\mathcal{O}(1)\).---   Determine whether the cursor points to a point within the tree.-locate :: Cursor a -> Location-locate (Cursor _ Nothing Nil) = Outside-locate _                      = Inside   
src/Data/RadixNTree/Word8/Strict.hs view
@@ -77,19 +77,6 @@   , subtree1   , prefix1 -  , Point (..)-  , Cursor (..)-  , stop--  , Location (..)-  , locate--  , cursor0-  , move0--  , cursor1-  , move1-   , lookupL0   , lookupL1 @@ -330,7 +317,6 @@ import           Data.RadixNTree.Word8.Key import           Radix.Common import           Radix.Exception-import           Radix.Word8.Common import           Radix.Word8.Foundation  import           Control.Applicative@@ -1206,98 +1192,6 @@         Nil           -> Nil      Just _  -> Tip (fromStep step w z) mx t------ | Current position in the tree.-data Point = -- | Above a node.-             Seam--             -- | In the middle of a 'Tip'.-           | Plane-               {-# UNPACK #-} !Int       -- ^ Always greater than @0@ and smaller than-                                         --   the length of the 'ByteArray'.-               {-# UNPACK #-} !ByteArray---- | A particular point in the tree.-data Cursor a = -- | This is effectively a 'Tip' where the 'ByteArray' is optional.-                Cursor-                  {-# UNPACK #-} !Point-                  {-# UNPACK #-} !(Maybe a)-                  !(Radix1Tree a)--instance Show a => Show (Cursor a) where-  showsPrec d c =-    showParen (d > 10) $-      showString "Cursor " . showsPrec 11 (stop c)--cursor0 :: RadixTree a -> Cursor a-cursor0 (RadixTree mx t) = Cursor Seam mx t--cursor1 :: Radix1Tree a -> Cursor a-cursor1 = Cursor Seam Nothing--{-# INLINE move0 #-}-move0 :: Feed -> Cursor a -> Cursor a-move0 (Feed feed) = \c ->-  feed $ \step s ->-    case step s of-      More w z -> move_ step w z c-      Done     -> c--{-# INLINE move1 #-}-move1 :: Feed1 -> Cursor a -> Cursor a-move1 (Feed1 w feed) = feed $ \step -> move_ step w--{-# INLINE move_ #-}-move_ :: (x -> Step Word8 x) -> Word8 -> x -> Cursor a -> Cursor a-move_ step = \w s (Cursor point mx dx) ->-  case point of-    Seam        -> go w s dx-    Plane i arr -> goarr arr mx dx w s i-  where-    go !w !s t =-      case t of-        Bin p l r     -> go w s $ if w < p-                                    then l-                                    else r--        Tip brr my dy -> goarr brr my dy w s 0--        Nil           -> Cursor Seam Nothing Nil--    goarr arr mx dx = goarr_-      where-        goarr_ w !s n-          | w == indexByteArray arr n =-              let !n' = n + 1-              in case step s of-                   More v z-                     | n' >= sizeofByteArray arr -> go v z dx-                     | otherwise                 -> goarr_ v z n'--                   Done      ->-                     let !point'-                           | n' >= sizeofByteArray arr = Seam-                           | otherwise                 = Plane n' arr--                     in Cursor point' mx dx--          | otherwise = Cursor Seam Nothing Nil---- | \(\mathcal{O}(1)\).---   Retrieve the value at which the cursor points.-stop :: Cursor a -> Maybe a-stop (Cursor point mx _) =-  case point of-    Seam -> mx-    _    -> Nothing---- | \(\mathcal{O}(1)\).---   Determine whether the cursor points to a point within the tree.-locate :: Cursor a -> Location-locate (Cursor _ Nothing Nil) = Outside-locate _                      = Inside   
+ src/Data/RadixNTree/Word8/Strict/Pointer.hs view
@@ -0,0 +1,159 @@+{-# LANGUAGE BangPatterns+           , DeriveLift+           , RankNTypes+           , ScopedTypeVariables #-}++module Data.RadixNTree.Word8.Strict.Pointer+  ( Pointer (..)+  , pointer0+  , pointer1++  , follow0+  , follow1+  ) where++import           Data.ByteArray.NonEmpty+import           Data.RadixNTree.Word8.Key+import           Data.RadixNTree.Word8.Strict++import           Control.Monad.ST+import           Data.Bits+import           Data.Primitive.ByteArray+import           Data.Word+import           Language.Haskell.TH.Syntax++++-- | Pure compressed tree reference.+--+--   @since 1.1+data Pointer = Pointer+                 {-# UNPACK #-} !Word -- ^ Node depth (0 is root).+                 !ByteArray           -- ^ Little-endian bitmask of size @depth@.+                                      --   'Bin' choices are represented as 0 and 1 for+                                      --   left and right respectively;+                                      --   'Tip's can hold any data.+               deriving (Show, Lift)++++-- | Mark a bit at the given depth as @1@.+mark :: MutableByteArray s -> Word -> ST s ()+mark marr n = do+  let x = fromIntegral $ n `unsafeShiftR` 3++      y = fromIntegral $ n .&. 0x07++  i <- readByteArray marr x++  let i' = i .|. unsafeShiftL 1 y++  writeByteArray marr x (i' :: Word8)++-- | Check if the bit at the given depth is @0@.+left :: ByteArray -> Word -> Bool+left arr n =+  let x = fromIntegral $ n `unsafeShiftR` 3++      y = fromIntegral $ n .&. 0x07++  in (unsafeShiftR (indexByteArray arr x :: Word8) y) .&. 0x1 == 0++-- | Create a bitmask that can hold @depth@ bits and populate it.+form :: (forall s. MutableByteArray s -> ST s ()) -> Word -> ByteArray+form go n = do+  runST $ do+    let m = fromIntegral (n `unsafeShiftR` 3) + 1+    marr <- newByteArray m+    fillByteArray marr 0 m 0x00+    go marr+    unsafeFreezeByteArray marr++++{-# INLINE pointer0 #-}+pointer0 :: Feed -> RadixTree a -> Maybe Pointer+pointer0 (Feed feed) = \(RadixTree mx t) ->+  feed $ \step s ->+    case step s of+      More w z -> pointer_ step w z t+      Done     ->+        case mx of+          Just _  -> Just $ Pointer 0 emptyByteArray+          Nothing -> Nothing++{-# INLINE pointer1 #-}+pointer1 :: Feed1 -> Radix1Tree a -> Maybe Pointer+pointer1 (Feed1 w feed) = feed $ \step -> pointer_ step w++{-# INLINE pointer_ #-}+pointer_+  :: (x -> Step Word8 x)+  -> Word8 -> x -> Radix1Tree a -> Maybe Pointer+pointer_ (step :: x -> Step Word8 x) = go 0 (\_ -> pure ())+  where+    go :: Word -> (forall s. MutableByteArray s -> ST s ())+       -> Word8 -> x -> Radix1Tree a -> Maybe Pointer+    go !i acc !w !s t =+      case t of+        Bin p l r ->+          if w < p+            then go (i + 1)                          acc       w s l+            else go (i + 1) (\marr -> mark marr i >> acc marr) w s r++        Tip arr mx dx -> goarr w s 0+          where+            goarr v !z n+              | v == indexByteArray arr n =+                  let n' = n + 1+                  in if n' >= sizeofByteArray arr+                       then case step z of+                              More u z' -> go (i + 1) acc u z' dx+                              Done      ->+                                case mx of+                                  Just _  -> Just $ Pointer (i + 1) (form acc i)+                                  Nothing -> Nothing++                       else case step z of+                              More u z' -> goarr u z' n'+                              Done      -> Nothing++              | otherwise = Nothing++        Nil -> Nothing++++follow0 :: a -> Pointer -> RadixTree a -> a+follow0 d (Pointer len arr) (RadixTree mx dx)+  | len == 0  = case mx of+                  Just x  -> x+                  Nothing -> d++  | otherwise = follow_ d len arr dx++follow1 :: a -> Pointer -> Radix1Tree a -> a+follow1 d (Pointer len arr) = follow_ d len arr++follow_ :: a -> Word -> ByteArray -> Radix1Tree a -> a+follow_ d len arr = go 0+  where+    go !i t =+      case t of+        Bin _ l r ->+          go (i + 1) $ if left arr i+                         then l+                         else r++        Tip _ mx dx ->+          let i' = i + 1+          in if i' > len+               then d+               else if i' == len+                      then case mx of+                             Just x  -> x+                             Nothing -> d++                      else go i' dx++        Nil -> d
src/Data/RadixTree/Word8/Lazy.hs view
@@ -67,21 +67,6 @@   , Data.RadixTree.Word8.Lazy.member   , subtree -    -- *** Chunked-    ---    -- | Chunked lookup allows providing the key piece by piece while retaining-    --   the ability to check for early failure.-    ---    --   Note that while 'subtree' can be used to achieve the same result,-    --   it is more expensive allocation-wise, as it must ensure that-    --   the resulting tree is well-formed after each chunk application.-  , Cursor-  , cursor-  , move-  , stop-  , Location (..)-  , locate-     -- ** Insert   , insert   , insertWith@@ -453,18 +438,6 @@ --   Prefix the root of the tree with the given key. prefix :: Feed -> RadixTree a -> RadixTree a prefix = prefix0----- | \(\mathcal{O}(1)\).---   Make a cursor that points to the root of the tree.-cursor :: RadixTree a -> Cursor a-cursor = cursor0--{-# INLINE move #-}--- | \(\mathcal{O}(\min(x,k))\).---   Move the cursor down by the extent of the given key.-move :: Feed -> Cursor a -> Cursor a-move = move0   
src/Data/RadixTree/Word8/Strict.hs view
@@ -66,21 +66,6 @@   , Data.RadixTree.Word8.Strict.member   , subtree -    -- *** Chunked-    ---    -- | Chunked lookup allows providing the key piece by piece while retaining-    --   the ability to check for early failure.-    ---    --   Note that while 'subtree' can be used to achieve the same result,-    --   it is more expensive allocation-wise, as it must ensure that-    --   the resulting tree is well-formed after each chunk application.-  , Cursor-  , cursor-  , move-  , stop-  , Location (..)-  , locate-     -- ** Insert   , insert   , insertWith@@ -488,19 +473,7 @@ prefix = prefix0  --- | \(\mathcal{O}(1)\).---   Make a cursor that points to the root of the tree.-cursor :: RadixTree a -> Cursor a-cursor = cursor0 -{-# INLINE move #-}--- | \(\mathcal{O}(\min(x,k))\).---   Move the cursor down by the extent of the given key.-move :: Feed -> Cursor a -> Cursor a-move = move0--- {-# INLINE insert #-} -- | \(\mathcal{O}(\min(x,k))\). --   Insert a new value in the tree at the given key.@@ -575,7 +548,7 @@ -- | \(\mathcal{O}(\min(x,k))\). --   Update the part of the tree at the given prefix. -----   The resulting 'RadixTree' is evaluated to WHNF.+--   The resulting t'RadixTree' is evaluated to WHNF. shape :: (RadixTree a -> RadixTree a) -> Feed -> RadixTree a -> RadixTree a shape = shape0 
+ src/Data/RadixTree/Word8/Strict/Pointer.hs view
@@ -0,0 +1,38 @@+{-| Compressed references for spine-strict radix trees.++    Pointers have a much smaller memory footprint and+    allow faster lookup in trees that are known to never change shape.+ -}++module Data.RadixTree.Word8.Strict.Pointer+  ( Pointer+  , pointer+  , follow+  ) where++import           Data.RadixNTree.Word8.Key (Feed)+import           Data.RadixNTree.Word8.Strict (RadixTree)+import           Data.RadixNTree.Word8.Strict.Pointer++++{-# INLINE pointer #-}+-- | \(\mathcal{O}(\min(x,k))\).+--   Create a pointer that mirrors an existing key.+--+--   The pointer is only guaranteed to behave correctly for any tree that holds the+--   same set of keys as the provided one.+--+--   @since 1.1+pointer :: Feed -> RadixTree a -> Maybe Pointer+pointer = pointer0++++-- | \(\mathcal{O}(\log n)\).+--   Look up the value at a pointer in the tree, falling back to the given default value+--   if it does not exist.+--+--   @since 1.1+follow :: a -> Pointer -> RadixTree a -> a+follow = follow0
src/Data/RadixTree/Word8/Strict/Unsafe.hs view
@@ -13,9 +13,9 @@     'Data.Patricia.Word.Strict.Unsafe.Patricia' tree, hence the definitions of     'Bin' and 'Nil' remain unchanged. -    The only state the resulting 'Radix1Tree' is unable to represent is the+    The only state the resulting t'Radix1Tree' is unable to represent is the     value at the root of the tree (for which the key is an empty byte sequence),-    as such that value is prepended with a special 2-tuple named 'RadixTree'.+    as such that value is prepended with a special 2-tuple named t'RadixTree'.  -}  module Data.RadixTree.Word8.Strict.Unsafe
− src/Radix/Word8/Common.hs
@@ -1,10 +0,0 @@-module Radix.Word8.Common-  ( Location (..)-  ) where------ | Whether the cursor point to a point within the tree.-data Location = Inside-              | Outside-                deriving Show
test/properties/Test/RadixTree/Word8/Lazy.hs view
@@ -5,6 +5,7 @@   ) where  import qualified Data.Radix1Tree.Word8.Lazy as Radix1+import qualified Data.Radix1Tree.Word8.Lazy.Zipper as Radix1 import           Data.RadixTree.Word8.Lazy (RadixTree) import qualified Data.RadixTree.Word8.Lazy as Radix import           Data.RadixTree.Word8.Lazy.Debug@@ -16,6 +17,7 @@  import           Data.Functor.Identity import qualified Data.List as List+import           Data.List.NonEmpty (NonEmpty (..)) import           Data.Word import           Test.Hspec @@ -136,25 +138,71 @@ subtreeT :: Eq a => TreeT [Word8] a subtreeT = Test treeEq (Radix.subtree . Radix.feedBytes) No.subtree -moveSingleT :: Eq a => IdT [Word8] a (Maybe a)-moveSingleT =-  Test (==) (\k -> Radix.stop . Radix.move (Radix.feedBytes k) . Radix.cursor)+descendLookup1T :: Eq a => IdT [Word8] a (Maybe a)+descendLookup1T =+  Test (==) (\k r -> let Radix.RadixTree mx t = r+                     in case k of+                          []   -> mx+                          l:ls -> do+                            ctx <- Radix1.descend (Radix1.feedBytes (l :| ls)) (Left t)+                            (v, _) <- Radix1.focus ctx+                            Just v+            )             No.lookup -moveThirdsT :: Eq a => IdT [Word8] a (Maybe a)-moveThirdsT =-  let thirds xs = let len = length xs+descendLookup3T :: Eq a => IdT [Word8] a (Maybe a)+descendLookup3T =+  let downward    []  mayEi = mayEi+      downward (k:ks) mayEi = do+        ei <- mayEi+        Right <$> Radix1.descend (Radix1.feedBytes (k :| ks)) ei++  in Test (==) (\xs r ->+                  let Radix.RadixTree mx t = r++                      len = length xs                       ~(as, ys) = List.splitAt (len `quot` 3) xs                       ~(bs, cs) = List.splitAt (len `quot` 3) ys -                  in Radix.move (Radix.feedBytes cs)-                   . Radix.move (Radix.feedBytes bs)-                   . Radix.move (Radix.feedBytes as)+                  in case downward cs . downward bs $ downward as (Just $ Left t) of+                       Just (Right ctx) -> fst <$> Radix1.focus ctx+                       Just (Left _)    -> mx+                       _                -> Nothing+               )+               No.lookup -  in Test (==) (\k -> Radix.stop . thirds k . Radix.cursor) No.lookup+descendAdjust3T :: (Eq a, Integral a) => TreeT ([Word8], a) a+descendAdjust3T =+  let downward    []  mayEi = mayEi+      downward (k:ks) mayEi = do+        ei <- mayEi+        Right <$> Radix1.descend (Radix1.feedBytes (k :| ks)) ei +  in Test treeEq (\(xs, a) r ->+                     let Radix.RadixTree mx t = r +                         len = length xs+                         ~(as, ys) = List.splitAt (len `quot` 3) xs+                         ~(bs, cs) = List.splitAt (len `quot` 3) ys +                     in case downward cs . downward bs $ downward as (Just $ Left t) of+                          Just (Right ctx) -> do+                            case Radix1.focus ctx of+                              Nothing            -> r+                              Just (x, reinsert) ->+                                Radix.RadixTree mx $ reinsert (x + fromIntegral a)++                          Just (Left _)    ->+                            case mx of+                              Nothing -> r+                              Just x  -> Radix.RadixTree (Just $ x + fromIntegral a) t++                          _                -> r+                 )+                 (\(k, a) -> No.adjust (+ fromIntegral a) k)+++ prefixT :: Eq a => TreeT [Word8] a prefixT = Test treeEq (Radix.prefix . Radix.feedBytes) No.prefix @@ -704,8 +752,9 @@     it "find"             $ run unary1  findT     it "member"           $ run unary1_ memberT     it "subtree"          $ run unary1_ subtreeT-    it "move/single"      $ run unary1_ moveSingleT-    it "move/thirds"      $ run unary1_ moveThirdsT+    it "descend/lookup1"  $ run unary1_ descendLookup1T+    it "descend/lookup3"  $ run unary1_ descendLookup3T+    it "descend/adjust3"  $ run unary1  descendAdjust3T     it "insert"           $ run unary1  insertT     it "insertWith"       $ run unary1  insertWithT     it "adjust"           $ run unary1  adjustT
test/properties/Test/RadixTree/Word8/Strict.hs view
@@ -5,10 +5,12 @@   ) where  import qualified Data.Radix1Tree.Word8.Strict as Radix1+import qualified Data.Radix1Tree.Word8.Strict.Zipper as Radix1 import           Data.RadixTree.Word8.Strict (RadixTree) import qualified Data.RadixTree.Word8.Strict as Radix import           Data.RadixTree.Word8.Strict.Debug import qualified Data.RadixTree.Word8.Strict.Unsafe as Radix+import qualified Data.RadixTree.Word8.Strict.Pointer as Radix import           No.Tree (NoTree) import qualified No.Tree as No import           Test.Kit@@ -16,6 +18,7 @@  import           Data.Functor.Identity import qualified Data.List as List+import           Data.List.NonEmpty (NonEmpty (..)) import           Data.Word import           Test.Hspec @@ -134,25 +137,78 @@ subtreeT :: Eq a => TreeT [Word8] a subtreeT = Test treeEq (Radix.subtree . Radix.feedBytes) No.subtree -moveSingleT :: Eq a => IdT [Word8] a (Maybe a)-moveSingleT =-  Test (==) (\k -> Radix.stop . Radix.move (Radix.feedBytes k) . Radix.cursor)+pointerT :: Eq a => IdT ([Word8], a) a (Maybe a)+pointerT = Test (==) (\(k, i) r -> do ptr <- Radix.pointer (Radix.feedBytes k) r+                                      Just $ Radix.follow i ptr r+                     )+                     (\(k, _) r -> No.lookup k r)++descendLookup1T :: Eq a => IdT [Word8] a (Maybe a)+descendLookup1T =+  Test (==) (\k r -> let Radix.RadixTree mx t = r+                     in case k of+                          []   -> mx+                          l:ls -> do+                            ctx <- Radix1.descend (Radix1.feedBytes (l :| ls)) (Left t)+                            (v, _) <- Radix1.focus ctx+                            Just v+            )             No.lookup -moveThirdsT :: Eq a => IdT [Word8] a (Maybe a)-moveThirdsT =-  let thirds xs = let len = length xs+descendLookup3T :: Eq a => IdT [Word8] a (Maybe a)+descendLookup3T =+  let downward    []  mayEi = mayEi+      downward (k:ks) mayEi = do+        ei <- mayEi+        Right <$> Radix1.descend (Radix1.feedBytes (k :| ks)) ei++  in Test (==) (\xs r ->+                  let Radix.RadixTree mx t = r++                      len = length xs                       ~(as, ys) = List.splitAt (len `quot` 3) xs                       ~(bs, cs) = List.splitAt (len `quot` 3) ys -                  in Radix.move (Radix.feedBytes cs)-                   . Radix.move (Radix.feedBytes bs)-                   . Radix.move (Radix.feedBytes as)+                  in case downward cs . downward bs $ downward as (Just $ Left t) of+                       Just (Right ctx) -> fst <$> Radix1.focus ctx+                       Just (Left _)    -> mx+                       _                -> Nothing+               )+               No.lookup -  in Test (==) (\k -> Radix.stop . thirds k . Radix.cursor) No.lookup+descendAdjust3T :: (Eq a, Integral a) => TreeT ([Word8], a) a+descendAdjust3T =+  let downward    []  mayEi = mayEi+      downward (k:ks) mayEi = do+        ei <- mayEi+        Right <$> Radix1.descend (Radix1.feedBytes (k :| ks)) ei +  in Test treeEq (\(xs, a) r ->+                     let Radix.RadixTree mx t = r +                         len = length xs+                         ~(as, ys) = List.splitAt (len `quot` 3) xs+                         ~(bs, cs) = List.splitAt (len `quot` 3) ys +                     in case downward cs . downward bs $ downward as (Just $ Left t) of+                          Just (Right ctx) -> do+                            case Radix1.focus ctx of+                              Nothing            -> r+                              Just (x, reinsert) ->+                                Radix.RadixTree mx $ reinsert (x + fromIntegral a)++                          Just (Left _)    ->+                            case mx of+                              Nothing -> r+                              Just x  -> Radix.RadixTree (Just $ x + fromIntegral a) t++                          _                -> r+                 )+                 (\(k, a) -> No.adjust (+ fromIntegral a) k)++++ prefixT :: Eq a => TreeT [Word8] a prefixT = Test treeEq (Radix.prefix . Radix.feedBytes) No.prefix @@ -720,8 +776,10 @@     it "find"             $ run unary1  findT     it "member"           $ run unary1_ memberT     it "subtree"          $ run unary1_ subtreeT-    it "move/single"      $ run unary1_ moveSingleT-    it "move/thirds"      $ run unary1_ moveThirdsT+    it "pointer1       "  $ run unary1  pointerT+    it "descend/lookup1"  $ run unary1_ descendLookup1T+    it "descend/lookup3"  $ run unary1_ descendLookup3T+    it "descend/adjust3"  $ run unary1  descendAdjust3T     it "insert"           $ run unary1  insertT     it "insertWith"       $ run unary1  insertWithT     it "insertWith'"      $ run unary1  insertWithT'