blob: b9eac2002165bd21ad2f18657baf811b7ccc4946 (
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
|
module MergeSort (
msortBy
) where
infixl 7 :%
infixr 6 :&
data LenList a = LL {-# UNPACK #-} !Int Bool [a]
data LenListAnd a b = {-# UNPACK #-} !(LenList a) :% b
data Stack a
= End
| {-# UNPACK #-} !(LenList a) :& (Stack a)
msortBy :: (a -> a -> Ordering) -> [a] -> [a]
msortBy cmp = mergeSplit End where
splitAsc n _ _ _ | n `seq` False = undefined
splitAsc n as _ [] = LL n True as :% []
splitAsc n as a bs@(b:bs') = case cmp a b of
GT -> LL n False as :% bs
_ -> splitAsc (n + 1) as b bs'
splitDesc n _ _ _ | n `seq` False = undefined
splitDesc n rs a [] = LL n True (a:rs) :% []
splitDesc n rs a bs@(b:bs') = case cmp a b of
GT -> splitDesc (n + 1) (a:rs) b bs'
_ -> LL n True (a:rs) :% bs
mergeLL (LL na fa as) (LL nb fb bs) = LL (na + nb) True $ mergeLs na as nb bs where
mergeLs nx _ ny _ | nx `seq` ny `seq` False = undefined
mergeLs 0 _ ny ys = if fb then ys else take ny ys
mergeLs _ [] ny ys = if fb then ys else take ny ys
mergeLs nx xs 0 _ = if fa then xs else take nx xs
mergeLs nx xs _ [] = if fa then xs else take nx xs
mergeLs nx xs@(x:xs') ny ys@(y:ys') = case cmp x y of
GT -> y:mergeLs nx xs (ny - 1) ys'
_ -> x:mergeLs (nx - 1) xs' ny ys
push ssx px@(LL nx _ _) = case ssx of
End -> px :% ssx
py@(LL ny _ _) :& ssy -> case ssy of
End
| nx >= ny -> mergeLL py px :% ssy
pz@(LL nz _ _) :& ssz
| nx >= ny || nx + ny >= nz -> case nx > nz of
False -> push ssy $ mergeLL py px
_ -> case push ssz $ mergeLL pz py of
pz' :% ssz' -> push (pz' :& ssz') px
_ -> px :% ssx
mergeAll _ px | px `seq` False = undefined
mergeAll ssx px@(LL nx _ xs) = case ssx of
End -> xs
py@(LL _ _ _) :& ssy -> case ssy of
End -> case mergeLL py px of
LL _ _ xys -> xys
pz@(LL nz _ _) :& ssz -> case nx > nz of
False -> mergeAll ssy $ mergeLL py px
_ -> case push ssz $ mergeLL pz py of
pz' :% ssz' -> mergeAll (pz' :& ssz') px
mergeSplit ss _ | ss `seq` False = undefined
mergeSplit ss [] = case ss of
End -> []
px :& ss' -> mergeAll ss' px
mergeSplit ss as@(a:as') = case as' of
[] -> mergeAll ss $ LL 1 True as
b:bs -> case cmp a b of
GT -> case splitDesc 2 [a] b bs of
px :% rs -> case push ss px of
px' :% ss' -> mergeSplit (px' :& ss') rs
_ -> case splitAsc 2 as b bs of
px :% rs -> case push ss px of
px' :% ss' -> mergeSplit (px' :& ss') rs
{-# INLINABLE mergeSplit #-}
|