import Prelude hiding (zip, zipWith)

--zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
--zipWith _ [] [] = []
--zipWith f (x:xs) (y:ys) = f x y : (zipWith f xs ys)

data BTree a = Leaf a | Fork (BTree a) (BTree a) deriving (Show, Eq)

foo = Fork (Fork (Fork (Fork (Leaf 1) (Leaf 2)) (Leaf 3)) (Leaf 4)) (Leaf 5)
bar = Fork (Fork (Fork (Fork (Leaf 5) (Leaf 4)) (Leaf 3)) (Leaf 2)) (Leaf 1)

zipWithBTrees :: (a -> b -> c) -> BTree a -> BTree b -> BTree c
zipWithBTrees f (Leaf x) (Leaf y) = Leaf (f x y)
zipWithBTrees f (Fork xt xt') (Fork yt yt') = Fork (zipWithBTrees f xt yt) (zipWithBTrees f xt' yt')

data List a = Nil | Cons a (List a) deriving (Show, Eq)

ls0 = Cons 0 (Cons 1 (Cons 2 Nil))
ls1 = Cons 2 (Cons 1 (Cons 0 Nil))

zipWithLists :: (a -> b -> c) -> List a -> List b -> List c
zipWithLists _ Nil Nil = Nil
zipWithLists f (Cons x xs) (Cons y ys) = Cons (f x y) (zipWithLists f xs ys)

data RoseTree a = Node a [RoseTree a] deriving (Show, Eq)

xt = Node 0 [Node 1 [], Node 2 [Node 3 [], Node 4 [Node 5 []]]]
yt = Node 5 [Node 4 [], Node 3 [Node 2 [], Node 1 [Node 0 []]]]

zipWithRoseTrees :: (a -> b -> c) -> RoseTree a -> RoseTree b -> RoseTree c
zipWithRoseTrees f (Node x []) (Node y []) = Node (f x y) []
zipWithRoseTrees f (Node x xs) (Node y ys) = Node (f x y) (zipWith (zipWithRoseTrees f) xs ys)

class Zippable z where
  zipWith :: (a -> b -> c) -> z a -> z b -> z c
  zip :: z a -> z b -> z (a, b)
  zip = zipWith (,)

instance Zippable [] where
  zipWith _ [] [] = []
  zipWith f (x:xs) (y:ys) = f x y : (zipWith f xs ys)

instance Zippable List where
  zipWith _ Nil Nil = Nil
  zipWith f (Cons x xs) (Cons y ys) = Cons (f x y) (zipWith f xs ys)  

instance Zippable BTree where
  zipWith f (Leaf x) (Leaf y) = Leaf (f x y)
  zipWith f (Fork xt xt') (Fork yt yt') = Fork (zipWith f xt yt) (zipWith f xt' yt')

instance Zippable RoseTree where  
  zipWith f (Node x []) (Node y []) = Node (f x y) []
  zipWith f (Node x xs) (Node y ys) = Node (f x y) (zipWith (zipWith f) xs ys)