summaryrefslogtreecommitdiff
path: root/src/Data/Word64RangeMap.hs
blob: 141feb8c988761e02441e78778f385ab26181476 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ConstraintKinds #-}
-- |
-- A Word64RangeMap is actually a sorted array of arrays
-- but lookup is done via binary search testing the range.
-- One way of implementing a sparse array i suppose.
module Word64RangeMap where

import Data.Word
import Data.Array.Unboxed
import qualified Data.Array.MArray as MA
import Data.Array.MArray (MArray(..))

import Debug.Trace
import Data.Array.IO

type OuterIndex = Word64
type Index = Word64
type InnerArray b = UArray Index b
-- convenient contraint kind
type RangeArray rangeArray m b = (IArray UArray b, MArray rangeArray (InnerArray b) m)
-- The RangeMap type, to be used with the above constraint
type RangeMap rangeArray b =  rangeArray Index (InnerArray b)

-- | a sample RangeMap for easier debugging
getM :: IO (RangeMap IOArray Word8)
getM = newListArray (0,3) [ listArray (0,5) [0..5]
                          , listArray (15,20) [15..20]
                          , listArray (100,105) [100..105]
                          , listArray (106,107) [106..107]
                          ]

lookupArray :: RangeArray ra m b => Index -> RangeMap ra b -> m (Either OuterIndex (OuterIndex,InnerArray b))
lookupArray i r = do
    (zr,nr) <- getBounds r -- bounds of mutable range array
    let (dr,mr) = (nr+1 - zr) `divMod` 2
    lookupArrayHelper i r (zr+dr+mr) zr nr

lookupArrayHelper :: RangeArray ra m b => Index -> RangeMap ra b
                                       -> OuterIndex{- current position  -}
                                       -> OuterIndex{- smallest possible -}
                                       -> OuterIndex{- largest possible  -}
                                       -> m (Either OuterIndex (OuterIndex,InnerArray b))
lookupArrayHelper i ra pos min max = do
    {- trace ("lookupArrayHelper " ++ show i ++ " "
                                ++ "ra "
                                ++ show pos ++ " "
                                ++ show min ++ " "
                                ++ show max) (return ()) -}
    ca <- MA.readArray ra pos -- current array
    let beforeOrAfter i (x,y) | i < x = LT
        beforeOrAfter i (x,y) | i > y = GT
        beforeOrAfter _ _ = EQ
        avg a b = (a + b) `divMod` 2
        isUnequalMod2 a b = if a `mod` 2 /= b `mod` 2 then 1 else 0
        f a b recurse finish
                = let (pos',r) = avg a b
                      u = isUnequalMod2 r (max - min) {- trace ("u = " ++ show t 
                                                                       ++ " min=" ++ show min 
                                                                       ++ " max=" ++ show max
                                                                       ++ " pos'=" ++ show pos'
                                                                       ++ " r=" ++ show r
                                                                       ) t -}
                      in if max - min + u > 1 && pos' /= pos then recurse pos' r else finish
        s = "beforeOrAfter " ++ show i ++ " " ++ show (bounds ca) ++ " --> "
        checkAndReturn pos = do
            a <- MA.readArray ra pos
            {- trace ("checkAndReturn " ++ show pos ++ " bounds a= " ++ show (bounds a)) (return ()) -}
            case beforeOrAfter i (bounds a) of
                EQ -> {- trace "EQ" $ -} return (Right (pos,a))
                LT -> {- trace "LT" $ -} return (Left pos)
                GT -> {- trace "GT" $ -} return (Left (pos+1))
    case beforeOrAfter i (bounds ca) of
        GT -> {- trace (s ++ "GT") $ -} f pos max (\pos' r -> lookupArrayHelper i ra (pos'+r) pos max) (checkAndReturn (pos + 1))
        LT -> {- trace (s ++ "LT") $ -} f min pos (\pos' r -> lookupArrayHelper i ra pos'     min pos) (checkAndReturn pos)
        EQ -> {- trace (s ++ "EQ") $ -} return (Right (pos,ca))

-- | up to the caller to ensure the inserted array does not overlap with others
--   and also to ensure that sort is maintained
--   TODO: Does not grow array!
insertArrayNoCheck :: RangeArray ra m b => OuterIndex -> InnerArray b -> RangeMap ra b -> m ()
insertArrayNoCheck pos e ra = do
    (z,n) <- MA.getBounds ra
    flip mapM_ [n .. pos + 1] $ \j -> do
        x <- readArray ra (j-1)
        writeArray ra j x
    writeArray ra pos e


insertWhereItGoes :: RangeArray ra m e => InnerArray e -> ra Index (InnerArray e) -> m (Either (OuterIndex, InnerArray e) ())
insertWhereItGoes e ra = do
    let (i,j) = bounds e
    (z,n) <- MA.getBounds ra
    let inside x (low,hi) = x >= low && x <= hi
    r <- lookupArray i ra 
    case r of
        Left ii -> if ii+1 <= n
                    then do
                        y <- MA.readArray ra (ii+1)
                        if j `inside` bounds y
                            then return (Left (ii+1,y)) -- TODO, actually could shrink y or e
                            else Right <$> insertArrayNoCheck ii e ra
                    else Right <$> insertArrayNoCheck ii e ra
        Right x -> return (Left x)

lookupInRangeMap k ra = do
    b <- lookupArray k ra
    case b of
        Right (_,ar) -> return (Just (ar ! k))
        Left _ -> return Nothing