-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathBatched.hs
98 lines (86 loc) · 4.07 KB
/
Batched.hs
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
module Data.Ranking.Batched (BRanking(..), batch, sizeRanking, bindR, bindUR, bindRI, bindURI, composeRI, composeURI) where
import Data.Array
import Data.Function
import Data.List
import qualified Data.Ranking as R
-- rank and unrank size items to/from 0..size-1
data BRanking a = BRanking {
size :: Integer,
cmp :: a -> a -> Ordering,
unrank :: [Integer] -> [a],
rank :: [a] -> [Integer]
}
-- (trivially) turn a function returning a ranking into one returning a batched ranking
batch :: (a -> R.Ranking b) -> a -> BRanking b
batch arb a = let r = arb a in BRanking {
size = R.size r,
cmp = R.cmp r,
unrank = map (R.unrank r),
rank = map (R.rank r)
}
-- rank pairs (0,0)..(0,size0-1), (1,0)..(1,size1-1), ... ,(k,0)..(k,sizek-1)
sizeRanking :: (Show a, Eq a) => [(a, Integer)] -> BRanking (a, Integer)
sizeRanking itemSizes = BRanking size cmp unrank rank where
size = sum . map snd $ itemSizes
cmp (a0,i0) (a1,i1) = if cmpai == EQ then compare i0 i1 else cmpai where
cmpai = compare ai0 ai1
(Just ai0) = elemIndex a0 items
(Just ai1) = elemIndex a1 items
items = map fst itemSizes
unrank = urnk 0 itemSizes where
urnk _ _ [] = []
urnk sum iS@((a,sz):iS') is@(i:is') = if i-sum < sz then (a,i-sum):urnk sum iS is' else urnk (sum+sz) iS' is
rank = rnk 0 itemSizes where
rnk _ _ [] = []
rnk sum [] ais@((a,i):ais') = error $ "nothing left to rank " ++ show (a,i)
rnk sum iS@((a',sz):iS') ais@((a,i):ais') = if a==a' then (sum+i):rnk sum iS ais' else rnk (sum+sz) iS' ais
-- group snd's of pairs with same fst
groupByFst :: (Eq a) => [(a,b)] -> [(a,[b])]
groupByFst = map (\xs@((x,_):_) -> (x, map snd xs)). groupBy ((==) `on` fst)
-- test if list has nondecreasing values
nondecreasing :: (Ord a) => [a] -> Bool
nondecreasing l = and [x <= y | (x:y:_) <- tails l]
-- analogue of monadic bind for arbitrarily sized Rankings
bindR :: (b -> a) -> BRanking a -> (a -> BRanking b) -> BRanking b
bindR inv ra arb = BRanking sz cmpr unrnk rnk where
aranks = [0.. size ra-1]
sizeR = sizeRanking $ zip aranks (map (size . arb) . unrank ra $ aranks)
sz = size sizeR
cmpr b0 b1 = if cmp ra a0 a1 == EQ then cmp (arb a0) b0 b1 else cmp ra a0 a1 where
(a0, a1) = (inv b0, inv b1)
unrnk is = concat . zipWith unrank rbs . map snd $ aibis where
rbs = map arb . unrank ra . map fst $ aibis
aibis = groupByFst . unrank sizeR $ is
rnk bs = rank sizeR [(ai,bi) | (ai, bs) <- aibs, let rb = arb . inv . head $ bs, bi <- rank rb bs] where
ais = rank ra . map inv $ bs
-- aibs = if nondecreasing ais then groupByFst (zip ais bs) else error ("decreasing " ++ show ais)
aibs = groupByFst (zip ais bs)
-- analogue of monadic bind for Uniformly sized Rankings
bindUR :: (b -> a) -> BRanking a -> (a -> BRanking b) -> BRanking b
bindUR inv ra arb = BRanking sz cmpr unrnk rnk where
sz = sizeRa * sizeRb
sizeRa = size ra
sizeRb = size . arb . head . unrank ra $ [0]
cmpr b0 b1 = if cmp ra a0 a1 == EQ then cmp (arb a0) b0 b1 else cmp ra a0 a1 where
(a0, a1) = (inv b0, inv b1)
unrnk is = concat . zipWith unrank rbs . map snd $ aibis where
rbs = map arb . unrank ra . map fst $ aibis
aibis = groupByFst . map (`divMod` sizeRb) $ is
rnk bs = [(ai*sizeRb+bi) | (ai, bs) <- aibs, let rb = arb . inv . head $ bs, bi <- rank rb bs] where
ais = rank ra . map inv $ bs
-- aibs = if nondecreasing ais then groupByFst (zip ais bs) else error ("decreasing " ++ show ais)
aibs = groupByFst (zip ais bs)
-- non-uniform ranking bind with identity inverse
bindRI :: BRanking a -> (a -> BRanking a) -> BRanking a
bindRI = bindR id
-- uniform ranking bind with identity inverse
bindURI :: BRanking a -> (a -> BRanking a) -> BRanking a
bindURI = bindUR id
-- non-uniform ranking bind with identity inverse
composeRI :: (a -> BRanking a) -> (a -> BRanking a) -> (a -> BRanking a)
composeRI f g = \x -> f x `bindRI` g
infixr 1 `composeRI`
-- uniform ranking bind with identity inverse
composeURI :: (a -> BRanking a) -> (a -> BRanking a) -> (a -> BRanking a)
composeURI f g = \x -> f x `bindURI` g
infixr 2 `composeURI`