module Data.Heap (
	
	Heap, MinHeap, MaxHeap,
	HeapPolicy(..), MinPolicy, MaxPolicy,
	
	null, isEmpty, size, head,
	
	empty, singleton,
	insert, deleteHead, extractHead,
	
	union, unions,
	
	
	fromList, toList, elems,
	
	fromAscList, toAscList,
	
	check
) where
import Data.List (foldl')
import Data.Monoid
import Data.Ord
import Prelude hiding (head, null)
data Heap p a
	= Empty
	| Tree  !Int a !(Heap p a) !(Heap p a)
type MinHeap a = Heap MinPolicy a
type MaxHeap a = Heap MaxPolicy a
instance (Show a) => Show (Heap p a) where
	show h = "fromList " ++ (show . toList) h
instance (HeapPolicy p a) => Eq (Heap p a) where
	h1 == h2 = EQ == compare h1 h2
instance (HeapPolicy p a) => Ord (Heap p a) where
	compare h1 h2 = compare' (toAscList h1) (toAscList h2)
		where	compare' [] [] = EQ
			compare' [] _  = LT
			compare' _  [] = GT
			compare' (x:xs) (y:ys) = case heapCompare (policy h1) x y of
				EQ -> compare' xs ys
				c  -> c
instance (HeapPolicy p a) => Monoid (Heap p a) where
	mempty  = empty
	mappend = union
	mconcat = unions
class HeapPolicy p a where
	
	
	
	heapCompare :: p -> a -> a -> Ordering
data MinPolicy = MinPolicy
instance (Ord a) => HeapPolicy MinPolicy a where
	heapCompare = const compare
data MaxPolicy = MaxPolicy
instance (Ord a) => HeapPolicy MaxPolicy a where
	heapCompare = const (flip compare)
null :: Heap p a -> Bool
null Empty = True
null _     = False
isEmpty :: Heap p a -> Bool
isEmpty = null
rank :: Heap p a -> Int
rank Empty          = 0
rank (Tree r _ _ _) = r
policy :: Heap p a -> p
policy = const undefined
size :: (Num n) => Heap p a -> n
size Empty = 0
size (Tree _ _ a b) = 1 + size a + size b
head :: (HeapPolicy p a) => Heap p a -> a
head = fst . extractHead
empty :: Heap p a
empty = Empty
singleton :: a -> Heap p a
singleton x = Tree 1 x empty empty
insert :: (HeapPolicy p a) => a -> Heap p a -> Heap p a
insert x h = union h (singleton x)
deleteHead :: (HeapPolicy p a) => Heap p a -> Heap p a
deleteHead = snd . extractHead
extractHead :: (HeapPolicy p a) => Heap p a -> (a, Heap p a)
extractHead Empty          = (error "Heap is empty", Empty)
extractHead (Tree _ x a b) = (x, union a b)
union :: (HeapPolicy p a) => Heap p a -> Heap p a -> Heap p a
union h Empty = h
union Empty h = h
union heap1@(Tree _ x l1 r1) heap2@(Tree _ y l2 r2) = if LT == heapCompare (policy heap1) x y
	then makeT x l1 (union r1 heap2) 
	else makeT y l2 (union r2 heap1) 
makeT :: a -> Heap p a -> Heap p a -> Heap p a
makeT x a b = let
		ra = rank a
		rb = rank b
	in if ra > rb
		then Tree (rb + 1) x a b
		else Tree (ra + 1) x b a
unions :: (HeapPolicy p a) => [Heap p a] -> Heap p a
unions = foldl' union empty
fromList :: (HeapPolicy p a) => [a] -> Heap p a
fromList = unions . (map singleton)
toList :: Heap p a -> [a]
toList Empty          = []
toList (Tree _ x a b) = x : toList a ++ toList b
elems :: Heap p a -> [a]
elems = toList
fromAscList :: (HeapPolicy p a) => [a] -> Heap p a
fromAscList = fromList 
toAscList :: (HeapPolicy p a) => Heap p a -> [a]
toAscList Empty            = []
toAscList h@(Tree _ x a b) = x : mergeLists (toAscList a) (toAscList b)
	where	mergeLists [] ys = ys
		mergeLists xs [] = xs
		mergeLists xs@(x:xs') ys@(y:ys') = if LT == heapCompare (policy h) x y
	      		then x : mergeLists xs' ys
			else y : mergeLists xs  ys'
check :: (HeapPolicy p a) => Heap p a -> Bool
check Empty = True
check h@(Tree r x left right) = let
		leftRank  = rank left
		rightRank = rank right
	in (isEmpty left || LT /= heapCompare (policy h) (head left) x)
		&& (isEmpty right || LT /= heapCompare (policy h) (head right) x)
		&& r == 1 + rightRank
		&& leftRank >= rightRank
		&& check left
		&& check right