-
Notifications
You must be signed in to change notification settings - Fork 7
/
Ranking.hs
121 lines (108 loc) · 4.87 KB
/
Ranking.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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
module Data.Ranking (Ranking(..), multinomialRanking, finiteRanking, listRanking, sizeRanking, bindR, bindUR, bindRI, bindURI, composeRI, composeURI) where
import Data.Ord
import Data.Array
import Data.Maybe
import Data.List
-- rank and unrank size items to/from 0..size-1
data Ranking a = Ranking {
size :: Integer,
cmp :: a -> a -> Ordering,
unrank :: Integer -> a,
rank :: a -> Integer
}
-- lexicographically rank lists containing given numbers of elements
multinomialRanking :: Eq a => [(a, Int)] -> Ranking [a]
multinomialRanking itemCounts = Ranking size cmp unrank rank where
cnts = map snd itemCounts
total = sum cnts
size = fac total `div` product (map fac cnts) where
fac n = fac_!n where
fac_ = listArray (0,total) (scanl (*) 1 [1..fromIntegral total])
cmp [] [] = EQ
cmp (a:as) (b:bs) = if cmpiab == EQ then cmp as bs else cmpiab where
cmpiab = compare ia ib
(Just ia) = elemIndex a items
(Just ib) = elemIndex b items
items = map fst itemCounts
unrank = place total itemCounts size where
place 0 _ _ 0 = []
place 0 _ _ _ = error "nonzero i"
place n itemCounts size i = setItem id itemCounts i where
setItem pref (ic@(item,cnt):ics) i = if i < size'
then item : place (n-1) (pref ((item,cnt-1):ics)) size' i
else setItem (pref . (ic:)) ics (i - size') where
size' = size * (fromIntegral cnt) `div` (fromIntegral n)
rank = unplace total itemCounts size where
unplace :: Eq a => Int -> [(a,Int)]-> Integer -> [a] -> Integer
unplace 0 _ _ l = if null l then 0 else error "non-null list"
unplace n itemCounts size (item:items)= rnkItem id itemCounts where
rnkItem pref (ic@(it,cnt):ics) = if it == item
then unplace (n-1) (pref ((item,cnt-1):ics)) size' items
else size' + rnkItem (pref . (ic:)) ics where
size' = size * (fromIntegral cnt) `div` (fromIntegral n)
-- rank Integers from 0..size-1
finiteRanking :: Integer -> Ranking Integer
finiteRanking size = Ranking size compare id id
-- rank itens from list
listRanking :: (Eq a, Show a) => [a] -> Ranking a
listRanking l = Ranking size cmp unrank rank where
size = fromIntegral $ length l
cmp a b = compare ia ib where
(Just ia) = elemIndex a l
(Just ib) = elemIndex b l
unrank i = l !! fromIntegral i
rank x = case elemIndex x l of
Nothing -> error $ show x ++ " not in " ++ show l
Just i -> fromIntegral i
-- rank pairs (0,0)..(0,size0-1), (1,0)..(1,size1-1), ... ,(k,0)..(k,sizek-1)
sizeRanking :: Eq a => [(a, Integer)] -> Ranking (a, Integer)
sizeRanking itemSizes = Ranking 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 = exhaust itemSizes where
exhaust ((a,sz):iS) i = if i < sz then (a,i) else exhaust iS (i - sz)
rank (a,i) = i + sum (map snd (takeWhile ((/= a) . fst) itemSizes))
-- analogue of functor function fmap for Rankings
mapR :: (b -> a) -> (a -> b) -> Ranking a -> Ranking b
mapR inv f ra = Ranking sz cmpr unrnk rnk where
sz = size ra
cmpr b0 b1 = cmp ra (inv b0) (inv b1)
unrnk = f . unrank ra
rnk = rank ra . inv
-- analogue of monadic bind for arbitrarily sized Rankings
bindR :: (b -> a) -> Ranking a -> (a -> Ranking b) -> Ranking b
bindR inv ra arb = Ranking sz cmpr unrnk rnk where
sizeR = sizeRanking [(i, size . arb . unrank ra $ i) | i <- [0..size ra-1]]
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 i = uncurry (unrank . arb . unrank ra) (unrank sizeR i)
rnk b = let a = inv b in rank sizeR (rank ra a, rank (arb a) b)
-- analogue of monadic bind for Uniformly sizes Rankings
bindUR :: (b -> a) -> Ranking a -> (a -> Ranking b) -> Ranking b
bindUR inv ra arb = Ranking sz cmpr unrnk rnk where
sz = sizeRa * sizeRb
sizeRa = size ra
sizeRb = size (arb (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 i = uncurry (unrank . arb . unrank ra) (i `divMod` sizeRb)
rnk b = let a = inv b in rank ra a * sizeRb + rank (arb a) b
-- non-uniform ranking bind with identity inverse
bindRI :: Ranking a -> (a -> Ranking a) -> Ranking a
bindRI = bindR id
-- uniform ranking bind with identity inverse
bindURI :: Ranking a -> (a -> Ranking a) -> Ranking a
bindURI = bindUR id
-- non-uniform ranking bind with identity inverse
composeRI :: (a -> Ranking a) -> (a -> Ranking a) -> (a -> Ranking a)
composeRI f g = \x -> f x `bindRI` g
infixr 1 `composeRI`
-- uniform ranking bind with identity inverse
composeURI :: (a -> Ranking a) -> (a -> Ranking a) -> (a -> Ranking a)
composeURI f g = \x -> f x `bindURI` g
infixr 2 `composeURI`