File: IntTrie.hs

package info (click to toggle)
haskell-data-inttrie 0.1.4-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 64 kB
  • sloc: haskell: 107; makefile: 2
file content (181 lines) | stat: -rw-r--r-- 7,316 bytes parent folder | download | duplicates (4)
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
-------------------------------------------------------------
-- |
-- Module        : Data.IntTrie
-- Copyright     : (c) Luke Palmer 2010
-- License       : BSD3
--
-- Maintainer    : Luke Palmer <lrpalmer@gmail.com>
-- Stability     : experimental
-- Portability   : Haskell 2010
--
-- Provides a minimal infinite, lazy trie for integral types.
-- It intentionally leaves out ideas such as delete and
-- emptiness so that it can be used lazily, eg. as the target
-- of an infinite foldr.  Essentially its purpose is to be an
-- efficient implementation of a function from integral type,
-- given point-at-a-time modifications.
-------------------------------------------------------------

module Data.IntTrie 
    ( IntTrie, identity, apply, modify, modify', overwrite,
      mirror, modifyAscList, modifyDescList )
where

import Control.Applicative
import Control.Arrow (first, second)
import Data.Bits
import Data.Function (fix)
import Data.Monoid (Monoid(..))
import Data.Semigroup (Semigroup(..))

-- | A trie from integers to values of type a. 
-- 
-- Semantics: [[IntTrie a]] = Integer -> a
data IntTrie a = IntTrie (BitTrie a) a (BitTrie a)  -- negative, 0, positive

data BitTrie a = BitTrie a (BitTrie a) (BitTrie a)

instance Functor BitTrie where
    fmap f ~(BitTrie x l r) = BitTrie (f x) (fmap f l) (fmap f r)

instance Applicative BitTrie where
    pure x = fix (\g -> BitTrie x g g)
    ~(BitTrie f fl fr) <*> ~(BitTrie x xl xr) = BitTrie (f x) (fl <*> xl) (fr <*> xr)

instance Semigroup a => Semigroup (BitTrie a) where
    (<>) = liftA2 (<>)

instance Monoid a => Monoid (BitTrie a) where
    mempty = pure mempty
    mappend = liftA2 mappend

instance Functor IntTrie where
    fmap f ~(IntTrie neg z pos) = IntTrie (fmap f neg) (f z) (fmap f pos)

instance Applicative IntTrie where
    pure x = IntTrie (pure x) x (pure x)
    IntTrie fneg fz fpos <*> IntTrie xneg xz xpos = 
        IntTrie (fneg <*> xneg) (fz xz) (fpos <*> xpos)

instance Semigroup a => Semigroup (IntTrie a) where
    (<>) = liftA2 (<>)

instance Monoid a => Monoid (IntTrie a) where
    mempty = pure mempty
    mappend = liftA2 mappend

-- | Apply the trie to an argument.  This is the semantic map.
apply :: (Ord b, Num b, Bits b) => IntTrie a -> b -> a
apply (IntTrie neg z pos) x =
    case compare x 0 of
        LT -> applyPositive neg (-x)
        EQ -> z
        GT -> applyPositive pos x

applyPositive :: (Num b, Bits b) => BitTrie a -> b -> a
applyPositive (BitTrie one even odd) x
    | x == 1 = one
    | testBit x 0 = applyPositive odd (x `shiftR` 1)
    | otherwise   = applyPositive even (x `shiftR` 1)

-- | The identity trie.  
--
-- > apply identity = id
identity :: (Num a, Bits a) => IntTrie a
identity = IntTrie (fmap negate identityPositive) 0 identityPositive

identityPositive :: (Num a, Bits a) => BitTrie a
identityPositive = go
    where
    go = BitTrie 1 (fmap (`shiftL` 1) go) (fmap (\n -> (n `shiftL` 1) .|. 1) go)

-- | Modify the function at one point
--
-- > apply (modify x f t) i | i == x = f (apply t i)
-- >                        | otherwise = apply t i
modify :: (Ord b, Num b, Bits b) => b -> (a -> a) -> IntTrie a -> IntTrie a
modify x f ~(IntTrie neg z pos) =
    case compare x 0 of
        LT -> IntTrie (modifyPositive (-x) f neg) z pos
        EQ -> IntTrie neg (f z) pos
        GT -> IntTrie neg z (modifyPositive x f pos)

modifyPositive :: (Num b, Bits b) => b -> (a -> a) -> BitTrie a -> BitTrie a
modifyPositive x f ~(BitTrie one even odd)
    | x == 1      = BitTrie (f one) even odd
    | testBit x 0 = BitTrie one even (modifyPositive (x `shiftR` 1) f odd)
    | otherwise   = BitTrie one (modifyPositive (x `shiftR` 1) f even) odd


-- | Modify the function at one point (strict version)
modify' :: (Ord b, Num b, Bits b) => b -> (a -> a) -> IntTrie a -> IntTrie a
modify' x f (IntTrie neg z pos) =
    case compare x 0 of
        LT -> (IntTrie $! modifyPositive' (-x) f neg) z pos
        EQ -> (IntTrie neg $! f z) pos
        GT -> IntTrie neg z $! modifyPositive' x f pos

modifyPositive' :: (Num b, Bits b) => b -> (a -> a) -> BitTrie a -> BitTrie a
modifyPositive' x f (BitTrie one even odd)
    | x == 1      = (BitTrie $! f one) even odd
    | testBit x 0 = BitTrie one even $! modifyPositive' (x `shiftR` 1) f odd
    | otherwise   = (BitTrie one $! modifyPositive' (x `shiftR` 1) f even) odd


-- | Overwrite the function at one point
--
-- > overwrite i x = modify i (const x)
overwrite :: (Ord b, Num b, Bits b) => b -> a -> IntTrie a -> IntTrie a
overwrite i x = modify i (const x)


-- | Negate the domain of the function
--
-- > apply (mirror t) i = apply t (-i)
-- > mirror . mirror = id
mirror :: IntTrie a -> IntTrie a
mirror ~(IntTrie neg z pos) = IntTrie pos z neg


-- | Modify the function at a (potentially infinite) list of points in ascending order
--
-- > modifyAscList [(i0, f0)..(iN, fN)] = modify i0 f0 . ... . modify iN fN
modifyAscList :: (Ord b, Num b, Bits b) => [(b, a -> a)] -> IntTrie a -> IntTrie a
modifyAscList ifs ~t@(IntTrie neg z pos) =
    case break ((>= 0) . fst) ifs of
        ([],   [])          -> t
        (nifs, (0, f):pifs) -> IntTrie (modifyAscListNegative nifs neg) (f z)
                                       (modifyAscListPositive pifs pos)
        (nifs, pifs)        -> IntTrie (modifyAscListNegative nifs neg) z
                                       (modifyAscListPositive pifs pos)
    where modifyAscListNegative = modifyAscListPositive . map (first negate) . reverse

-- | Modify the function at a (potentially infinite) list of points in descending order
modifyDescList :: (Ord b, Num b, Bits b) => [(b, a -> a)] -> IntTrie a -> IntTrie a
modifyDescList ifs = mirror . modifyAscList (map (first negate) ifs) . mirror

modifyAscListPositive :: (Ord b, Num b, Bits b) => [(b, a -> a)] -> BitTrie a -> BitTrie a
modifyAscListPositive [] t = t
modifyAscListPositive ((0, _):_) _ =
    error "modifyAscList: expected strictly monotonic indices"
modifyAscListPositive ifs@((i, f):_) ~(BitTrie one even odd) = BitTrie one' even' odd' where
    (one', ifs')      = if i == 1 then (f one, tail ifs) else (one, ifs)
    even'             = modifyAscListPositive ifsEven even
    odd'              = modifyAscListPositive ifsOdd  odd
    (ifsOdd, ifsEven) = both (map $ first (`shiftR` 1)) $ partitionIndices ifs'
    both f (x, y)     = (f x, f y)

-- Like `partition (flip testBit 0 . fst)`, except that this version addresses the
-- problem of infinite lists of only odd or only even indices by injecting an `id`
-- into the other result list wherever there are two evens or two odds in a row.
-- This allows `modifyAscListPositive` to return a value as soon as the next index is
-- higher than the current location in the trie instead of scanning for the end of
-- the list, which for infinite lists may never be reached.
partitionIndices :: (Num b, Bits b) => [(b, a -> a)] -> ([(b, a -> a)], [(b, a -> a)])
partitionIndices []           = ([], [])
partitionIndices [x]          = if testBit (fst x) 0 then ([x], []) else ([], [x])
partitionIndices (x:xs@(y:_)) = case testBit (fst x) 0 of
    False -> (if testBit (fst y) 0 then odd else pad:odd, x:even)
    True  -> (x:odd, if testBit (fst y) 0 then pad:even else even)
    where ~(odd, even) = partitionIndices xs
          pad = (fst y - 1, id)