Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swap function #8

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 1.1.1.0 -- December 2024

* Added `swap` function to strict PATRICIA and radix tree modules.

## 1.1.0.0 -- October 2024

* Added `Zipper` modules for non-empty radix trees;
Expand Down
6 changes: 6 additions & 0 deletions no/No/Tree.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

, insert
, insertWith
, swap
, adjust
, delete
, update
Expand Down Expand Up @@ -221,6 +222,11 @@
insertWith :: Ord k => (a -> a) -> k -> a -> NoTree k a -> NoTree k a
insertWith f k a = alter (Just . maybe a f) k

swap :: Ord k => k -> a -> NoTree k a -> (Maybe a, NoTree k a)
swap k a no =
let ~(NoTree as, mb, NoTree bs) = splitLookup k no
in (mb, NoTree $ as <> ((k, a) :<| bs))

adjust :: Ord k => (a -> a) -> k -> NoTree k a -> NoTree k a
adjust f = alter (fmap f)

Expand Down Expand Up @@ -340,7 +346,7 @@



data Range k = UnsafeRange

Check warning on line 349 in no/No/Tree.hs

View workflow job for this annotation

GitHub Actions / GHC 9.0 on ubuntu-latest

• Ignoring unusable UNPACK pragma

Check warning on line 349 in no/No/Tree.hs

View workflow job for this annotation

GitHub Actions / GHC 9.2 on ubuntu-latest

• Ignoring unusable UNPACK pragma

Check warning on line 349 in no/No/Tree.hs

View workflow job for this annotation

GitHub Actions / GHC 9.4 on ubuntu-latest

• Ignoring unusable UNPACK pragma
{-# UNPACK #-} !Openness
k
{-# UNPACK #-} !Openness
Expand Down
2 changes: 1 addition & 1 deletion radix-tree.cabal
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: radix-tree
version: 1.1.0.0
version: 1.1.1.0

category: Data Structures
synopsis: Radix trees
Expand Down
4 changes: 4 additions & 0 deletions src/Data/Patricia/Word/Strict.hs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ module Data.Patricia.Word.Strict
, insertWith
, insertWith'

-- | === Swap
, Swap (..)
, swap

-- ** Map
, adjust
, adjust'
Expand Down
41 changes: 41 additions & 0 deletions src/Data/Patricia/Word/Strict/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@
, insertWith
, insertWith'

, Swap (..)
, swap

, adjust
, adjust'

Expand Down Expand Up @@ -183,7 +186,7 @@
import Radix.Word.Common
import Radix.Word.Foundation

import Control.Applicative

Check warning on line 189 in src/Data/Patricia/Word/Strict/Internal.hs

View workflow job for this annotation

GitHub Actions / GHC 9.6 on ubuntu-latest

The import of ‘Control.Applicative’ is redundant

Check warning on line 189 in src/Data/Patricia/Word/Strict/Internal.hs

View workflow job for this annotation

GitHub Actions / GHC 9.8 on ubuntu-latest

The import of ‘Control.Applicative’ is redundant

Check warning on line 189 in src/Data/Patricia/Word/Strict/Internal.hs

View workflow job for this annotation

GitHub Actions / GHC 9.10 on ubuntu-latest

The import of ‘Control.Applicative’ is redundant
import Control.DeepSeq
import Control.Exception (throw)
import Data.Bits
Expand Down Expand Up @@ -1459,6 +1462,44 @@



-- | Result of a value swap.
data Swap a = Swap !(Maybe a) !(Patricia a)
deriving Show

-- | \(\mathcal{O}(\min(n,W))\).
-- Insert a new value in the tree at the given key.
-- If a value already exists at that key, it is returned alongside the tree.
swap :: Word -> a -> Patricia a -> Swap a
swap !w a = \t ->
let (# mb, t' #) = go t
in Swap mb t'
where
go t =
case t of
Bin p l r
| beyond p w -> let !t' = join w (Tip w a) p t
in (# Nothing, t'#)

| w < p -> let !(# mb, l' #) = go l
!t' = Bin p l' r
in (# mb, t' #)

| otherwise -> let !(# mb, r' #) = go r
!t' = Bin p l r'
in (# mb, t' #)

Tip k b
| k == w -> let !t' = Tip k a
in (# Just b, t' #)
| otherwise -> let !t' = join w (Tip w a) k t
in (# Nothing, t' #)

Nil -> let !t' = Tip w a
in (# Nothing, t' #)




-- | \(\mathcal{O}(\min(n,W))\).
-- Apply a function to a value in the tree at the given key.
adjust :: (a -> a) -> Word -> Patricia a -> Patricia a
Expand Down
12 changes: 12 additions & 0 deletions src/Data/Radix1Tree/Word8/Strict.hs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ module Data.Radix1Tree.Word8.Strict
, insertWith
, insertWith'

-- | === Swap
, Swap1 (..)
, swap

-- ** Map
, adjust
, adjust'
Expand Down Expand Up @@ -500,6 +504,14 @@ insertWith' :: (a -> a) -> Feed1 -> a -> Radix1Tree a -> Radix1Tree a
insertWith' = insertWith1'


{-# INLINE swap #-}
-- | \(\mathcal{O}(\min(x,k))\).
-- Insert a new value in the tree at the given key.
-- If a value already exists at that key, it is returned alongside the tree.
swap :: Feed1 -> a -> Radix1Tree a -> Swap1 a
swap = swap1


{-# INLINE adjust #-}
-- | \(\mathcal{O}(\min(x,k))\).
-- Apply a function to a value in the tree at the given key.
Expand Down
100 changes: 100 additions & 0 deletions src/Data/RadixNTree/Word8/Strict.hs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@
, insertWith1
, insertWith1'

, Swap (..)
, Swap1 (..)
, swap0
, swap1

, adjust0
, adjust0'

Expand Down Expand Up @@ -335,7 +340,7 @@
type StrictRadixTree = RadixTree

-- | Spine-strict radix tree with byte sequences as keys.
data RadixTree a = RadixTree

Check warning on line 343 in src/Data/RadixNTree/Word8/Strict.hs

View workflow job for this annotation

GitHub Actions / GHC 9.0 on ubuntu-latest

• Ignoring unusable UNPACK pragma

Check warning on line 343 in src/Data/RadixNTree/Word8/Strict.hs

View workflow job for this annotation

GitHub Actions / GHC 9.2 on ubuntu-latest

• Ignoring unusable UNPACK pragma

Check warning on line 343 in src/Data/RadixNTree/Word8/Strict.hs

View workflow job for this annotation

GitHub Actions / GHC 9.4 on ubuntu-latest

• Ignoring unusable UNPACK pragma
{-# UNPACK #-} !(Maybe a) -- ^ Value at the empty byte sequence key.
!(Radix1Tree a)

Expand Down Expand Up @@ -391,7 +396,7 @@
!(Radix1Tree a) -- ^ Masked bit is @0@. Invariant: not 'Nil'.
!(Radix1Tree a) -- ^ Masked bit is @1@. Invariant: not 'Nil'.

| Tip

Check warning on line 399 in src/Data/RadixNTree/Word8/Strict.hs

View workflow job for this annotation

GitHub Actions / GHC 9.0 on ubuntu-latest

• Ignoring unusable UNPACK pragma on the second argument of ‘Tip’

Check warning on line 399 in src/Data/RadixNTree/Word8/Strict.hs

View workflow job for this annotation

GitHub Actions / GHC 9.2 on ubuntu-latest

• Ignoring unusable UNPACK pragma on the second argument of ‘Tip’

Check warning on line 399 in src/Data/RadixNTree/Word8/Strict.hs

View workflow job for this annotation

GitHub Actions / GHC 9.4 on ubuntu-latest

• Ignoring unusable UNPACK pragma on the second argument of ‘Tip’
{-# UNPACK #-} !ByteArray -- ^ Invariant: non-empty.
{-# UNPACK #-} !(Maybe a) -- ^ Invariant: can only be 'Nothing' when
-- the tree below is 'Bin'.
Expand Down Expand Up @@ -4126,6 +4131,101 @@



-- | Result of a value swap.
data Swap a = Swap !(Maybe a) !(RadixTree a)
deriving Show

{-# INLINE swap0 #-}
swap0 :: Feed -> a -> RadixTree a -> Swap a
swap0 (Feed feed) a = \(RadixTree mx t) ->
feed $ \step s ->
case step s of
More w z -> let !(# mb, t' #) = swap_ a step w z t
in Swap mb $ RadixTree mx t'

Done -> Swap mx $ RadixTree (Just a) t


-- | Result of a value swap.
data Swap1 a = Swap1 !(Maybe a) !(Radix1Tree a)
deriving Show

{-# INLINE swap1 #-}
swap1 :: Feed1 -> a -> Radix1Tree a -> Swap1 a
swap1 (Feed1 w feed) a = \t ->
feed $ \step s ->
let !(# mb, t' #) = swap_ a step w s t
in Swap1 mb t'


{-# INLINE swap_ #-}
swap_
:: a -> (x -> Step Word8 x) -> Word8 -> x -> Radix1Tree a -> (# Maybe a, Radix1Tree a #)
swap_ a step = go
where
go !w !s t =
case t of
Bin p l r
| beyond p w -> let !t' = join
w (singleton_ step w s a)
p t

in (# Nothing, t' #)

| w < p -> let !(# mb, l' #) = go w s l
!t' = Bin p l' r
in (# mb, t' #)

| otherwise -> let !(# mb, r' #) = go w s r
!t' = Bin p l r'
in (# mb, t' #)

Tip arr mx dx -> goarr w s 0
where
goarr v !z n
| v == indexByteArray arr n =
if n + 1 >= sizeofByteArray arr
then case step z of
More u z' -> let !(# mb, dy #) = go u z' dx
!t' = Tip arr mx dy
in (# mb, t' #)

Done -> let !t' = Tip arr (Just a) dx
in (# mx, t' #)

else case step z of
More u z' -> goarr u z' (n + 1)
Done ->
let !(# !brr, !crr #) = splitByteArray 0 (n + 1) arr

!t' = Tip brr (Just a) (Tip crr mx dx)

in (# Nothing, t' #)

| n == 0 =
let !t' = join
(indexByteArray arr 0) t
w (singleton_ step w s a)

in (# Nothing, t' #)

| otherwise =
let !(# !brr, !crr #) = splitByteArray 0 n arr

!t' = Tip brr Nothing $
join
(indexByteArray crr 0) (Tip crr mx dx)
v (singleton_ step v z a)

in (# Nothing, t' #)

Nil -> let !t' = singleton_ step w s a
in (# Nothing, t' #)





{-# INLINE adjust0 #-}
adjust0 :: (a -> a) -> Feed -> RadixTree a -> RadixTree a
adjust0 f (Feed feed) = \(RadixTree mx t) ->
Expand Down
12 changes: 12 additions & 0 deletions src/Data/RadixTree/Word8/Strict.hs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ module Data.RadixTree.Word8.Strict
, insertWith
, insertWith'

-- | === Swap
, Swap (..)
, swap

-- ** Map
, adjust
, adjust'
Expand Down Expand Up @@ -498,6 +502,14 @@ insertWith' :: (a -> a) -> Feed -> a -> RadixTree a -> RadixTree a
insertWith' = insertWith0'


{-# INLINE swap #-}
-- | \(\mathcal{O}(\min(x,k))\).
-- Insert a new value in the tree at the given key.
-- If a value already exists at that key, it is returned alongside the tree.
swap :: Feed -> a -> RadixTree a -> Swap a
swap = swap0


{-# INLINE adjust #-}
-- | \(\mathcal{O}(\min(x,k))\).
-- Apply a function to a value in the tree at the given key.
Expand Down
11 changes: 11 additions & 0 deletions test/properties/Test/Patricia/Word/Strict.hs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ maxViewEq (Just (Pat.ViewR pat (Pat.Lookup k a))) (Just (no, l, b)) =
maxViewEq Nothing Nothing = True
maxViewEq _ _ = False

type SwapT s a =
Test s (Patricia a) (NoTree Word a)
(Pat.Swap a) (Maybe a, NoTree Word a)

swapEq :: Eq a => Pat.Swap a -> (Maybe a, NoTree Word a) -> Bool
swapEq (Pat.Swap mx pat) (my, no) = mx == my && treeEq pat no



lookupT, dirtyLookupT :: Eq a => IdT Word a (Maybe a)
Expand Down Expand Up @@ -165,6 +172,9 @@ insertWithT_ g =
let f x = (+ fromIntegral x)
in Test treeEq (\(k, a) -> g (f a) k a) (\(k, a) -> No.insertWith (f a) k a)

swapT :: Eq a => SwapT (Word, a) a
swapT = Test swapEq (uncurry Pat.swap) (uncurry No.swap)

adjustT, adjustT' :: (Eq a, Integral a) => TreeT (Word, a) a
adjustT = adjustT_ Pat.adjust
adjustT' = adjustT_ Pat.adjust'
Expand Down Expand Up @@ -773,6 +783,7 @@ test = do
it "insert" $ run unary1 insertT
it "insertWith" $ run unary1 insertWithT
it "insertWith'" $ run unary1 insertWithT'
it "swap" $ run unary1 swapT
it "adjust" $ run unary1 adjustT
it "adjust'" $ run unary1 adjustT'
it "delete" $ run unary1_ deleteT
Expand Down
11 changes: 11 additions & 0 deletions test/properties/Test/RadixTree/Word8/Strict.hs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@ maxViewEq (Just (Radix.ViewR t k a)) (Just (no, l, b)) =
maxViewEq Nothing Nothing = True
maxViewEq _ _ = False

type SwapT s a =
Test s (RadixTree a) (NoTree [Word8] a)
(Radix.Swap a) (Maybe a, NoTree [Word8] a)

swapEq :: Eq a => Radix.Swap a -> (Maybe a, NoTree [Word8] a) -> Bool
swapEq (Radix.Swap mx pat) (my, no) = mx == my && treeEq pat no



lookupT :: Eq a => IdT [Word8] a (Maybe a)
Expand Down Expand Up @@ -227,6 +234,9 @@ insertWithT_ g =
in Test treeEq (\(k, a) -> g (f a) (Radix.feedBytes k) a)
(\(k, a) -> No.insertWith (f a) k a)

swapT :: Eq a => SwapT ([Word8], a) a
swapT = Test swapEq (\(k, i) -> Radix.swap (Radix.feedBytes k) i) (uncurry No.swap)

adjustT, adjustT' :: (Eq a, Integral a) => TreeT ([Word8], a) a
adjustT = adjustT_ Radix.adjust
adjustT' = adjustT_ Radix.adjust'
Expand Down Expand Up @@ -783,6 +793,7 @@ test = do
it "insert" $ run unary1 insertT
it "insertWith" $ run unary1 insertWithT
it "insertWith'" $ run unary1 insertWithT'
it "swap" $ run unary1 swapT
it "adjust" $ run unary1 adjustT
it "adjust'" $ run unary1 adjustT'
it "delete" $ run unary1_ deleteT
Expand Down
Loading