```module Util.PartialFunctions where

import Data.List (nub)

import Util.Exception

-- *********************************************************************
-- Partial functions
-- *********************************************************************
type PartialFunction a b = [(a,b)] -- From a to b

functionDomain :: Eq a => PartialFunction a b -> [a]
functionDomain f = map fst f

functionImage :: Eq a => PartialFunction a b -> [b]
functionImage f = map snd f

identityFunction :: Eq a => [a] -> PartialFunction a a
identityFunction xs = [(x,x) | x <- xs]

invert :: (Eq a, Eq b) => PartialFunction a b -> PartialFunction b a
invert f = [(a,b) | (b,a) <- f]

apply :: Eq a => PartialFunction a b -> a -> b
apply f x =
let
pos = [b | (a,b) <- f, a == x]
in
if length pos == 0 then
panic ("Partial function applied to value outside of domain")

applyRelation :: Eq a => PartialFunction a b -> a -> [b]
applyRelation f x = [b | (a,b) <- f, a == x]

safeApply :: Eq a => PartialFunction a b -> a -> Maybe b
safeApply f x =
let
pos = [b | (a,b) <- f, a == x]
in
if length pos == 0 then Nothing

composeFunctions ::
(Eq a, Eq b) => PartialFunction b c -> PartialFunction a b ->
PartialFunction a c
composeFunctions f g =
[(a, apply f b) | (a,b) <- g]

mapPF :: Eq a => PartialFunction a b -> [a] -> [b]
mapPF f xs = [apply f x | x <- xs]

safeMapPF :: Eq a => PartialFunction a b -> [a] -> [b]
safeMapPF f xs = [x | Just x <- [safeApply f x | x <- xs]]

updatePF :: Eq a => PartialFunction a b -> a -> b -> PartialFunction a b
updatePF f a b = (a,b):[(c,d) | (c,d) <- f, c /= a]

removeEntry :: Eq a => PartialFunction a b -> a -> PartialFunction a b
removeEntry f a = [(c,d) | (c,d) <- f, c /= a]
```