summaryrefslogtreecommitdiff
path: root/src/Data
diff options
context:
space:
mode:
authorJames Crayne <jim.crayne@gmail.com>2018-05-23 07:05:35 +0000
committerJames Crayne <jim.crayne@gmail.com>2018-05-23 23:17:01 +0000
commit16851ba2417933dd83ee1e7b91964e7e58b78d85 (patch)
tree7cf7c9928eef0c486c73ee9f3c847ef63266f310 /src/Data
parentec009d9ebc0a68c14658d352c9936d6d8376a15c (diff)
Word64Rangemap fixes and improvements
Diffstat (limited to 'src/Data')
-rw-r--r--src/Data/Word64RangeMap.hs170
1 files changed, 121 insertions, 49 deletions
diff --git a/src/Data/Word64RangeMap.hs b/src/Data/Word64RangeMap.hs
index 141feb8c..b5cbe9d1 100644
--- a/src/Data/Word64RangeMap.hs
+++ b/src/Data/Word64RangeMap.hs
@@ -1,52 +1,102 @@
1{-# LANGUAGE FlexibleContexts #-} 1{-# LANGUAGE FlexibleContexts, FlexibleInstances #-}
2{-# LANGUAGE ConstraintKinds #-} 2{-# LANGUAGE ConstraintKinds #-}
3{-# LANGUAGE MultiParamTypeClasses #-}
4{-# LANGUAGE TupleSections #-}
3-- | 5-- |
4-- A Word64RangeMap is actually a sorted array of arrays 6-- A Word64RangeMap is actually a sorted array of arrays
5-- but lookup is done via binary search testing the range. 7-- but lookup is done via binary search testing the range.
6-- One way of implementing a sparse array i suppose. 8-- One way of implementing a sparse array i suppose.
7module Word64RangeMap where 9module Data.Word64RangeMap where
8 10
9import Data.Word 11import Data.Word
10import Data.Array.Unboxed 12import Data.Array.Unboxed
11import qualified Data.Array.MArray as MA 13import qualified Data.Array.MArray as MA
12import Data.Array.MArray (MArray(..)) 14import Data.Array.MArray (MArray(..))
13 15import qualified Data.Array.Base as Base
16import Data.Reference
14import Debug.Trace 17import Debug.Trace
15import Data.Array.IO 18import Data.Array.IO
19import Data.IORef
16 20
17type OuterIndex = Word64 21type OuterIndex = Int
18type Index = Word64 22type Index = Word64
19type InnerArray b = UArray Index b 23type InnerArray b = UArray Index b
24
25-- | Although this type includes a parameter for index, the code assumes bounds start at 0
26-- and the index has 'Integral', and 'Num' instances.
27newtype RefArray r ma i e = RefArray (r (ma i e))
28
20-- convenient contraint kind 29-- convenient contraint kind
21type RangeArray rangeArray m b = (IArray UArray b, MArray rangeArray (InnerArray b) m) 30type RangeArray rangeArray m b ref = (IArray UArray b, MArray rangeArray (InnerArray b) m, Reference ref m)
22-- The RangeMap type, to be used with the above constraint 31-- The RangeMap type, to be used with the above constraint
23type RangeMap rangeArray b = rangeArray Index (InnerArray b) 32type RangeMap rangeArray b ref = RefArray ref rangeArray OuterIndex (InnerArray b)
33
34insertArrayAt :: (MA.MArray ma e m, Reference r m, Ix i, Num i, Integral i) => RefArray r ma i e -> i -> e -> m ()
35insertArrayAt (RefArray ref) pos e = do
36 ma <- readRef ref
37 (0,n) <- MA.getBounds ma -- irrefutable pattern, assume array starts at 0
38 (esBefore,esAt) <- splitAt (fromIntegral pos) <$> (getElems ma)
39 newMA <- MA.newListArray (0,n+1) (esBefore ++ [e] ++ esAt)
40 writeRef ref newMA
24 41
42deleteArrayAt :: (MA.MArray ma e m, Reference r m, Ix i, Num i, Integral i) => RefArray r ma i e -> i -> m ()
43deleteArrayAt (RefArray ref) pos = do
44 ma <- readRef ref
45 (0,n) <- MA.getBounds ma -- irrefutable pattern, assume array starts at 0
46 (esBefore,esAt) <- splitAt (fromIntegral pos) <$> getElems ma
47 newMA <- MA.newListArray (0,n-1) (esBefore ++ drop 1 esAt)
48 writeRef ref newMA
49
50readRefArray (RefArray x) = readRef x
51writeRefArray (RefArray x) y = writeRef x y
52
53{-
54instance (Reference r m, MArray ma e m) => MArray (RefArray r ma) e m where
55 getBounds (RefArray r) = readRef r >>= Base.getBounds
56 getNumElements (RefArray r) = readRef r >>= Base.getNumElements
57 unsafeRead (RefArray r) i = readRef r >>= \x -> Base.unsafeRead x i
58 unsafeWrite (RefArray r) i e = modifyRef r (\x -> Base.unsafeWrite x i e >> return (x,()))
59-}
60
61-- | a sample RangeMap for easier debugging
62getX :: IO (RangeMap IOArray Word8 IORef)
63getX = RefArray <$>
64 (newIORef =<<
65 newListArray (0,2)
66 [ listArray (15,20) [15..20]
67 , listArray (100,105) [100..105]
68 , listArray (106,107) [106..107]
69 ])
25-- | a sample RangeMap for easier debugging 70-- | a sample RangeMap for easier debugging
26getM :: IO (RangeMap IOArray Word8) 71getY :: IO (RangeMap IOArray Word8 IORef)
27getM = newListArray (0,3) [ listArray (0,5) [0..5] 72getY = RefArray <$>
73 (newIORef =<<
74 newListArray (0,3)
75 [ listArray (0,5) [0..5]
28 , listArray (15,20) [15..20] 76 , listArray (15,20) [15..20]
29 , listArray (100,105) [100..105] 77 , listArray (100,105) [100..105]
30 , listArray (106,107) [106..107] 78 , listArray (106,107) [106..107]
31 ] 79 ])
32 80
33lookupArray :: RangeArray ra m b => Index -> RangeMap ra b -> m (Either OuterIndex (OuterIndex,InnerArray b)) 81lookupArray :: RangeArray ra m b ref => Index -> RangeMap ra b ref -> m (Either OuterIndex (OuterIndex,InnerArray b))
34lookupArray i r = do 82lookupArray i r = do
35 (zr,nr) <- getBounds r -- bounds of mutable range array 83 ra <- readRefArray r
84 (zr,nr) <- getBounds ra -- bounds of mutable range array
36 let (dr,mr) = (nr+1 - zr) `divMod` 2 85 let (dr,mr) = (nr+1 - zr) `divMod` 2
37 lookupArrayHelper i r (zr+dr+mr) zr nr 86 lookupArrayHelper i r (zr+dr+mr) zr nr
38 87
39lookupArrayHelper :: RangeArray ra m b => Index -> RangeMap ra b 88lookupArrayHelper :: RangeArray ra m b ref => Index -> RangeMap ra b ref
40 -> OuterIndex{- current position -} 89 -> OuterIndex{- current position -}
41 -> OuterIndex{- smallest possible -} 90 -> OuterIndex{- smallest possible -}
42 -> OuterIndex{- largest possible -} 91 -> OuterIndex{- largest possible -}
43 -> m (Either OuterIndex (OuterIndex,InnerArray b)) 92 -> m (Either OuterIndex (OuterIndex,InnerArray b))
44lookupArrayHelper i ra pos min max = do 93lookupArrayHelper i r pos min max = do
45 {- trace ("lookupArrayHelper " ++ show i ++ " " 94 {- trace ("lookupArrayHelper " ++ show i ++ " "
46 ++ "ra " 95 ++ "ra "
47 ++ show pos ++ " " 96 ++ show pos ++ " "
48 ++ show min ++ " " 97 ++ show min ++ " "
49 ++ show max) (return ()) -} 98 ++ show max) (return ()) -}
99 ra <- readRefArray r
50 ca <- MA.readArray ra pos -- current array 100 ca <- MA.readArray ra pos -- current array
51 let beforeOrAfter i (x,y) | i < x = LT 101 let beforeOrAfter i (x,y) | i < x = LT
52 beforeOrAfter i (x,y) | i > y = GT 102 beforeOrAfter i (x,y) | i > y = GT
@@ -55,8 +105,8 @@ lookupArrayHelper i ra pos min max = do
55 isUnequalMod2 a b = if a `mod` 2 /= b `mod` 2 then 1 else 0 105 isUnequalMod2 a b = if a `mod` 2 /= b `mod` 2 then 1 else 0
56 f a b recurse finish 106 f a b recurse finish
57 = let (pos',r) = avg a b 107 = let (pos',r) = avg a b
58 u = isUnequalMod2 r (max - min) {- trace ("u = " ++ show t 108 u = isUnequalMod2 r (max - min) {- trace ("u = " ++ show t
59 ++ " min=" ++ show min 109 ++ " min=" ++ show min
60 ++ " max=" ++ show max 110 ++ " max=" ++ show max
61 ++ " pos'=" ++ show pos' 111 ++ " pos'=" ++ show pos'
62 ++ " r=" ++ show r 112 ++ " r=" ++ show r
@@ -64,47 +114,69 @@ lookupArrayHelper i ra pos min max = do
64 in if max - min + u > 1 && pos' /= pos then recurse pos' r else finish 114 in if max - min + u > 1 && pos' /= pos then recurse pos' r else finish
65 s = "beforeOrAfter " ++ show i ++ " " ++ show (bounds ca) ++ " --> " 115 s = "beforeOrAfter " ++ show i ++ " " ++ show (bounds ca) ++ " --> "
66 checkAndReturn pos = do 116 checkAndReturn pos = do
67 a <- MA.readArray ra pos 117 boundsRA <- getBounds ra
68 {- trace ("checkAndReturn " ++ show pos ++ " bounds a= " ++ show (bounds a)) (return ()) -} 118 case beforeOrAfter pos boundsRA of
69 case beforeOrAfter i (bounds a) of 119 EQ -> do
70 EQ -> {- trace "EQ" $ -} return (Right (pos,a)) 120 a <- MA.readArray ra pos
121 {- trace ("checkAndReturn " ++ show pos ++ " bounds a= " ++ show (bounds a)) (return ()) -}
122 case beforeOrAfter i (bounds a) of
123 EQ -> {- trace "EQ" $ -} return (Right (pos,a))
124 LT -> {- trace "LT" $ -} return (Left pos)
125 GT -> {- trace "GT" $ -} return (Left (pos+1))
71 LT -> {- trace "LT" $ -} return (Left pos) 126 LT -> {- trace "LT" $ -} return (Left pos)
72 GT -> {- trace "GT" $ -} return (Left (pos+1)) 127 GT -> {- trace "GT" $ -} return (Left (pos+1))
73 case beforeOrAfter i (bounds ca) of 128 case beforeOrAfter i (bounds ca) of
74 GT -> {- trace (s ++ "GT") $ -} f pos max (\pos' r -> lookupArrayHelper i ra (pos'+r) pos max) (checkAndReturn (pos + 1)) 129 GT -> {- trace (s ++ "GT") $ -} f pos max (\pos' r' -> lookupArrayHelper i r (pos'+r') pos max) (checkAndReturn (pos + 1))
75 LT -> {- trace (s ++ "LT") $ -} f min pos (\pos' r -> lookupArrayHelper i ra pos' min pos) (checkAndReturn pos) 130 LT -> {- trace (s ++ "LT") $ -} f min pos (\pos' r' -> lookupArrayHelper i r pos' min pos) (checkAndReturn pos)
76 EQ -> {- trace (s ++ "EQ") $ -} return (Right (pos,ca)) 131 EQ -> {- trace (s ++ "EQ") $ -} return (Right (pos,ca))
77 132
78-- | up to the caller to ensure the inserted array does not overlap with others 133insertWhereItGoes :: RangeArray ra m e ref => InnerArray e -> RangeMap ra e ref -> m (Either (OuterIndex, InnerArray e) ())
79-- and also to ensure that sort is maintained 134insertWhereItGoes e r = do
80-- TODO: Does not grow array!
81insertArrayNoCheck :: RangeArray ra m b => OuterIndex -> InnerArray b -> RangeMap ra b -> m ()
82insertArrayNoCheck pos e ra = do
83 (z,n) <- MA.getBounds ra
84 flip mapM_ [n .. pos + 1] $ \j -> do
85 x <- readArray ra (j-1)
86 writeArray ra j x
87 writeArray ra pos e
88
89
90insertWhereItGoes :: RangeArray ra m e => InnerArray e -> ra Index (InnerArray e) -> m (Either (OuterIndex, InnerArray e) ())
91insertWhereItGoes e ra = do
92 let (i,j) = bounds e 135 let (i,j) = bounds e
93 (z,n) <- MA.getBounds ra 136 outerArray <- readRefArray r
94 let inside x (low,hi) = x >= low && x <= hi 137 (outerZ,outerN) <- MA.getBounds outerArray
95 r <- lookupArray i ra 138 let inside x (low,hi) = x >= low && x <= hi -- flip inRange
96 case r of 139 lr <- lookupArray i r
97 Left ii -> if ii+1 <= n 140 case lr of
141 Left ii ->
142 trace "(insertWhereItGoes) Left ii case"
143 $if ii <= outerN -- is there a next array?
98 then do 144 then do
99 y <- MA.readArray ra (ii+1) 145 y <- MA.readArray outerArray (ii)
100 if j `inside` bounds y 146 let boundsy = show (bounds y)
101 then return (Left (ii+1,y)) -- TODO, actually could shrink y or e 147 insideStr = show j ++ " inside " ++ boundsy
102 else Right <$> insertArrayNoCheck ii e ra 148 outsideStr = show j ++ " outside " ++ boundsy
103 else Right <$> insertArrayNoCheck ii e ra 149 if j `inside` bounds y -- does the final bound of inserted array overlap next array?
104 Right x -> return (Left x) 150 then trace insideStr $ return (Left (ii,y)) -- TODO, actually could shrink y or e
105 151 else trace outsideStr $ Right <$> insertArrayAt r ii e
106lookupInRangeMap k ra = do 152 else Right <$> insertArrayAt r ii e
107 b <- lookupArray k ra 153 Right x ->
154 trace "(insertWhereItGoes) Right x case"
155 $return (Left x)
156
157lookupInRangeMap :: RangeArray ra m e ref => Index -> RangeMap ra e ref -> m (Maybe e)
158lookupInRangeMap k r = do
159 b <- lookupArray k r
108 case b of 160 case b of
109 Right (_,ar) -> return (Just (ar ! k)) 161 Right (_,ar) -> return (Just (ar ! k))
110 Left _ -> return Nothing 162 Left _ -> return Nothing
163
164
165deleteRange :: RangeArray ra m e ref => (Index,Index)
166 -> RangeMap ra e ref
167 -> m (Either OuterIndex (OuterIndex,InnerArray e))
168
169deleteRange (z,n) r = do
170 result <- lookupArray z r
171 case result of
172 Left _ -> return result
173 Right (outerIndex,a) | bounds a /= (z,n) -> return (Left outerIndex)
174 Right (outerIndex,a) | bounds a == (z,n) -> do
175 outerArray <- readRefArray r
176 (zz,nn) <- getBounds outerArray
177 as <- map (\(i,e) -> (if i > outerIndex then i - 1 else i, e))
178 . filter ((/=outerIndex) . fst)
179 <$> getAssocs outerArray
180 mutAr <- MA.newListArray (zz,nn-1) (map snd as)
181 writeRefArray r mutAr
182 return result