module Lab10 where

{- EXERCISES -}

interleave :: a -> [a] -> [[a]]
interleave e [] = [[e]]
interleave e l@(x : xs) =
  let smallerInterleave = interleave e xs
  in  (e : l) : map (x :) smallerInterleave

permutations :: [a] -> [[a]]
permutations [] = [[]]
permutations (x : xs) =
  let smallerPerms = permutations xs
  in  concat $ map (interleave x) smallerPerms

type Edge a = (a, a)
data Graph a = Graph
  { vertices :: [a]
  , edges    :: [Edge a]
  }
  deriving Show

gr :: Graph Int
gr = Graph { vertices = [1 .. 6]
           , edges    = [(1, 2), (1, 5), (2, 3), (2, 5), (3, 4), (4, 5), (4, 6)]
           }

isPath :: Eq a => [Edge a] -> [a] -> Bool
isPath edges (x : y : xs) =
  ((x, y) `elem` edges || (y, x) `elem` edges) && isPath edges (y : xs)
isPath _ _ = True

findHamiltonian :: Eq a => Graph a -> [[a]]
findHamiltonian g =
  let vertexPerms = permutations (vertices g)
  in  filter (isPath (edges g)) vertexPerms

data DualNum a = Dn a a
  deriving (Eq, Ord)

instance Show a => Show (DualNum a) where
  show (Dn x x') = show x ++ " + " ++ show x' ++ "eps"

instance Num a => Num (DualNum a) where
  (Dn x x') + (Dn y y') = Dn (x + y) (x' + y')
  (Dn x x') * (Dn y y') = Dn (x * y) (x * y' + x' * y)
  abs (Dn x x') = Dn (abs x) (signum x * x')
  signum (Dn x x') = Dn (signum x) 0
  fromInteger i = Dn (fromInteger i) 0
  negate (Dn x x') = Dn (negate x) (negate x')

f :: Num a => a -> a
f x = x ^ 2 + 1

instance Fractional a => Fractional (DualNum a) where
  (Dn x x') / (Dn y y') = Dn (x / y) ((x' * y - x * y') / (y * y))
  fromRational r = Dn (fromRational r) 0

g :: Fractional a => a -> a
g x = (x ^ 2 - 2) / (x - 1)

sqr :: (Fractional a, Ord a) => a -> a
sqr x = convAbs $ iterate improve 1
 where
  improve r = (r + x / r) / 2
  convAbs (x1 : x2 : xs) | abs (x1 - x2) < 1e-10 = x2
                         | otherwise             = convAbs xs
  convAbs _ = error "end of infinite list"

{- TASKS -}

merge :: Ord b => (a -> b) -> [a] -> [a] -> [a]
merge _ xs [] = xs
merge _ [] ys = ys
merge f lx@(x : xs) ly@(y : ys) =
  let fx = f x
      fy = f y
  in  case compare fx fy of
        LT -> x : merge f xs ly
        EQ -> x : y : merge f xs ys
        GT -> y : merge f lx ys

subseqs :: [a] -> [[a]]
subseqs []       = [[]]
subseqs (x : xs) = merge length (subseqs xs) (map (x :) (subseqs xs))
