{- Cities, States and Countries -}

data City = Innsbruck | Munich | Graz | Gramais
data Country = Austria | Germany
data State = Tyrol | Bavaria | Styria

class Info a where
  population :: a -> Integer
  area :: a -> Integer

{- TODO: Make City, Country and State instances of Info -}

instance Info City where
  population = undefined
  area = undefined

instance Info Country where
  population = undefined
  area = undefined

instance Info State where
  population = undefined
  area = undefined

{- TODO: Define the following functions -}

inhabitantsPerSkm :: (Info a) => a -> Integer
inhabitantsPerSkm = undefined

hasMoreArea :: (Info a, Info b) => a -> b -> Bool
hasMoreArea = undefined

hasMoreInhabitants :: (Info a, Info b) => a -> b -> Bool
hasMoreInhabitants = undefined

{- TODO: Make Country and State instances of Located -}

class Located a where
  isIn :: City -> a -> Bool


{- Approximation of Pi -}

-- adjust type and defining equation
pi_lr :: Num a => Integer -> a
pi_lr k = 0

-- adjust type and defining equation
pi_rl :: Num a => Integer -> a
pi_rl k = 0

-- adjust defining equation
pi_opt :: (Eq a, Fractional a) => a
pi_opt = 0


{- Tests: just execute test_pi_lr, test_pi_rl, test_pi_opt, ,test_pi or test_locations
   you don't have to understand the definitions -}

test_list f s ys = all (test_fun f s) (zip test_nums ys) where
  test_nums = [0..10] ++ [1000..1010]
  test_fun f s (x, e) = abs (f x - e) < 0.0000001 ||
    error ("function " ++ s ++ " on input " ++ show x ++ " delivered " ++ show (f x) ++  
      ", but expected was " ++ show e)

test_pi_lr = test_list pi_lr "pi_lr" test_lr_f && test_list pi_lr "pi_lr" test_lr_d where
  test_lr_f = [2.6666667,2.8952382,2.9760463,3.017072,3.0418398,3.058403,3.0702548,3.0791535,3.0860798,3.0916238,3.0961616,3.141091,3.1410916,3.141092,3.1410925,3.141093,3.1410935,3.141094,3.1410944,3.141095,3.1410954,3.1410959] :: [Float]
  test_lr_d = [2.6666666666666665,2.895238095238095,2.976046176046176,3.017071817071817,3.041839618929402,3.0584027659273314,3.070254617779183,3.0791533941974256,3.0860798011238324,3.091623806667838,3.0961615264636406,3.141093153121444,3.1410936516248436,3.141094149134218,3.1410946456525384,3.141095141182763,3.141095635727838,3.1410961292906987,3.141096621874268,3.1410971134814583,3.141097604115169,3.1410980937782886] :: [Double]

test_pi_rl = test_list pi_rl "pi_rl" test_rl_f && test_list pi_rl "pi_rl" test_rl_d where
  test_rl_f = [2.6666667,2.8952382,2.9760463,3.017072,3.0418396,3.0584028,3.0702548,3.0791535,3.0860798,3.0916238,3.0961616,3.1410933,3.1410937,3.1410942,3.1410947,3.1410952,3.1410956,3.141096,3.1410966,3.141097,3.1410975,3.1410983] :: [Float]
  test_rl_d = [2.6666666666666665,2.895238095238095,2.976046176046176,3.017071817071817,3.041839618929402,3.058402765927332,3.0702546177791836,3.079153394197426,3.086079801123833,3.0916238066678385,3.096161526463641,3.141093153121449,3.1410936516248484,3.141094149134223,3.1410946456525437,3.141095141182768,3.1410956357278432,3.1410961292907036,3.141096621874273,3.141097113481463,3.1410976041151737,3.141098093778293] :: [Double]

test_pi_opt = let exp = 3.141384 :: Float in 
  pi_opt == exp || error ("evaluating pi_opt results in " ++ show pi_opt ++ ", but it should be computed as " ++ show exp)

test_pi = test_pi_lr && test_pi_rl && test_pi_opt

test_locations = hasMoreArea Austria Tyrol &&
                 inhabitantsPerSkm Gramais == 1 &&
                 hasMoreInhabitants Tyrol Gramais &&
                 not (hasMoreInhabitants Gramais Germany)

test_all = test_pi && test_locations