{-# LANGUAGE FlexibleContexts         #-}
{-# LANGUAGE TypeOperators            #-}
{-# LANGUAGE ScopedTypeVariables      #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  Generics.Regular.Functions.Fixpoints
-- Copyright   :  (c) 2009 Universiteit Utrecht
-- License     :  BSD3
--
-- Maintainer  :  generics@haskell.org
-- Stability   :  experimental
-- Portability :  non-portable
--
-- Summary: Auxiliary module for "Generics.Regular.Functions.Fixpoints".
-----------------------------------------------------------------------------

module Generics.Regular.Functions.Fixpoints (

    Fixpoints(..), fixpoints,
    Tree(..), foldTree, sumTree
    
  ) where

import Generics.Regular.Base


-- | Tree structure to store fixed points as found in the data type.
data Tree a = Leaf a | Node (Tree a) (Tree a)
 deriving Show

foldTree :: (a -> b) -> (b -> b -> b) -> Tree a -> b
foldTree l _ (Leaf x)    = l x
foldTree l n (Node x y)  = (foldTree l n x) `n` (foldTree l n y)

sumTree :: Tree Int -> Int
sumTree = foldTree id (+)

-- | The class to compute fixed points.
class Fixpoints f where 
    hFixpoints :: f a -> Tree Int

instance (Fixpoints f, Fixpoints g) => Fixpoints (f :+: g) where
    hFixpoints (_ :: (f :+: g) a) = 
      Node (hFixpoints (undefined :: f a))
           (hFixpoints (undefined :: g a))
    
instance (Fixpoints f, Constructor c) => Fixpoints (C c f) where
    hFixpoints (_ :: (C c f) a) = hFixpoints (undefined :: f a)

instance (Fixpoints f, Fixpoints g) => Fixpoints (f :*: g) where
    hFixpoints (_ :: (f :*: g) a) = 
      let Leaf m = hFixpoints (undefined :: f a)
          Leaf n = hFixpoints (undefined :: f b)
      in Leaf (m + n)

instance Fixpoints I where
    hFixpoints _ = Leaf 1

instance Fixpoints U where
    hFixpoints _ = Leaf 0

instance Fixpoints (K a) where
    hFixpoints _ = Leaf 0

-- | Return a tree structure of the fixed points of a datatype
fixpoints :: (Regular a, Fixpoints (PF a)) => a -> Tree Int
fixpoints x = hFixpoints (undefined `asTypeOf` (from x))