-----------------------------------------------------------------------------
-- |
-- Module      : Data.Invertible
-- License     : BSD-style
-- Maintainer  : 
-- Stability   : experimental
-- Portability : portable
-- 
-- This module provides invertible functions, or /bijections/.
--
-----------------------------------------------------------------------------
module Data.Invertible
        ( Invertible(..)
        , call, inverse
        ) where

import qualified Prelude as P
import Prelude          (Eq(..), Monad(..), ($), (&&), repeat, sequence, lookup, map, filter, Maybe(..), Bool, Show)
import Test.QuickCheck  (property, Arbitrary(..))
import System.IO.Unsafe (unsafePerformIO)
import Data.IORef       (newIORef, readIORef, writeIORef)
import Control.Exception(evaluate)

import Control.Category
import Control.Category.Defaults
import Control.Category.Internal.Comparable (Comparable(..))

-----------------------------------------------------------------------------
-- Type

-- | A function with an inverse
data Invertible a b = Invertible (a -> b) (b -> a)

-- | Use an invertible function
call :: InvArrow cat => Invertible a b -> cat a b
call (Invertible f g) = arrInv f g

  | The inverse of an ' Invertible ' function .
inverse :: InvArrow cat => Invertible a b -> cat b a
inverse (Invertible f g) = arrInv g f

-----------------------------------------------------------------------------
-- Instances: category classes

instance Category Invertible where
      id = Invertible id id
      Invertible f g . Invertible h i = Invertible (f . h) (i . g)

instance InvArrow Invertible where
      arrInv = Invertible

instance CategoryPair Invertible where
      swap = swap_InvArrow
      first  (Invertible f g) = Invertible (first  f) (first  g)
      second (Invertible f g) = Invertible (second f) (second g)
      Invertible f g *** Invertible h i = Invertible (f *** h) (g *** i)

instance CategoryChoice Invertible where
      mirror = mirror_InvArrow
      left  (Invertible f g) = Invertible (left  f) (left  g)
      right (Invertible f g) = Invertible (right f) (right g)
      Invertible f g +++ Invertible h i = Invertible (f +++ h) (g +++ i)

instance CategoryLoop Invertible where
      loop (Invertible f g) = Invertible (loop f) (loop g)

-----------------------------------------------------------------------------
-- Instances: QuickCheck

{-
-- This should perhaps be moved to Test.QuickCheck.Function

-- | An invertible function, with a table of used values
data InvertibleFunction a b = InvertibleFunction (FunctionTable a b) (a -> b) (b -> a)
-}

instance (Arbitrary a, Arbitrary b, Eq a, Eq b) => Arbitrary (Invertible a b) where
instance ( Arbitrary a , Arbitrary b , Eq a , Eq b , Show a , Show b ) = > Arbitrary ( Invertible a b ) where 
      arbitrary = do
            -- We need to generate a function and its inverse.
  One way to do this is to keep a map of all the values used ,
            --  while ensuring there are no duplicates
            -- We are going to need both arbitrary as and bs
            initialAs <- sequence (repeat arbitrary)
            initialBs <- sequence (repeat arbitrary)
  Magic in the IO Monad :
# NOINLINE ref #
                ref = unsafePerformIO $ newIORef ([], initialAs, initialBs)
                -- Add a to the table if it is not already there
                addA a = do  evaluate (a==a) -- fully force a
                             (assocs, as, bs) <- readIORef ref
                             case lookup a assocs of
                               Just b  -> return b
                               Nothing -> do
                                 let (b:bs') = bs
     " - > " P.++ P.show b
                                 writeIORef ref ((a,b):assocs, filter (/=a) as, filter (/=b) bs')
                                 return b
                addB b = do  evaluate (b==b)
                             (assocs, as, bs) <- readIORef ref
                             case lookup b (map swap assocs) of
                               Just a  -> return a
                               Nothing -> do
                                 let (a:as') = as
     " - > " P.++ P.show b
                                 writeIORef ref ((a,b):assocs, filter (/=a) as', filter (/=b) bs)
                                 return a
            -- Now make our function
            return $ Invertible (unsafePerformIO . addA) (unsafePerformIO . addB)
      
      coarbitrary (Invertible f g) = coarbitrary f . coarbitrary g

instance (Eq a, Eq b, Arbitrary a, Arbitrary b, Show a, Show b) => Comparable (Invertible a b) where
      Invertible f g `equals` Invertible h i
         = property $ \x y -> f x == h x && g y == i y


 -- | Test that the given Invertible function indeed has a proper inverse 
 prop_invertible : : ( Arbitrary a , Arbitrary b , Eq a , Eq b ) = > Invertible a a - > Property 
 prop_invertible f = f . inverse f ` equals ` i d 

 -- | Test the generation of invertible functions 
 prop_invertible : : Eq a = > Invertible a a - > a - > Bool 
 prop_invertible ( Invertible f g ) x = ( f .  ) x = = x & & ( g . f ) x = = x 

-- | Test that the given Invertible function indeed has a proper inverse
prop_invertible :: (Arbitrary a, Arbitrary b, Eq a, Eq b) => Invertible a a -> Property
prop_invertible f = f . inverse f `equals` id

-- | Test the generation of invertible functions
prop_invertible :: Eq a => Invertible a a -> a -> Bool
prop_invertible (Invertible f g) x = (f . g) x == x && (g . f) x == x
-}
---------------------------------------------------------------------------
 |
 Module      : Data.Invertible
 License     : BSD-style
 Maintainer  : 
 Stability   : experimental
 Portability : portable
 
 This module provides invertible functions, or /bijections/.

---------------------------------------------------------------------------
---------------------------------------------------------------------------
 Type
 | A function with an inverse
 | Use an invertible function
---------------------------------------------------------------------------
 Instances: category classes
---------------------------------------------------------------------------
 Instances: QuickCheck

-- This should perhaps be moved to Test.QuickCheck.Function

-- | An invertible function, with a table of used values
data InvertibleFunction a b = InvertibleFunction (FunctionTable a b) (a -> b) (b -> a)

 We need to generate a function and its inverse.
  while ensuring there are no duplicates
 We are going to need both arbitrary as and bs
 Add a to the table if it is not already there
 fully force a
 Now make our function
 | Test that the given Invertible function indeed has a proper inverse 
 | Test the generation of invertible functions 
 | Test that the given Invertible function indeed has a proper inverse
 | Test the generation of invertible functions

module Data.Invertible
        ( Invertible(..)
        , call, inverse
        ) where

import qualified Prelude as P
import Prelude          (Eq(..), Monad(..), ($), (&&), repeat, sequence, lookup, map, filter, Maybe(..), Bool, Show)
import Test.QuickCheck  (property, Arbitrary(..))
import System.IO.Unsafe (unsafePerformIO)
import Data.IORef       (newIORef, readIORef, writeIORef)
import Control.Exception(evaluate)

import Control.Category
import Control.Category.Defaults
import Control.Category.Internal.Comparable (Comparable(..))


data Invertible a b = Invertible (a -> b) (b -> a)

call :: InvArrow cat => Invertible a b -> cat a b
call (Invertible f g) = arrInv f g

  | The inverse of an ' Invertible ' function .
inverse :: InvArrow cat => Invertible a b -> cat b a
inverse (Invertible f g) = arrInv g f


instance Category Invertible where
      id = Invertible id id
      Invertible f g . Invertible h i = Invertible (f . h) (i . g)

instance InvArrow Invertible where
      arrInv = Invertible

instance CategoryPair Invertible where
      swap = swap_InvArrow
      first  (Invertible f g) = Invertible (first  f) (first  g)
      second (Invertible f g) = Invertible (second f) (second g)
      Invertible f g *** Invertible h i = Invertible (f *** h) (g *** i)

instance CategoryChoice Invertible where
      mirror = mirror_InvArrow
      left  (Invertible f g) = Invertible (left  f) (left  g)
      right (Invertible f g) = Invertible (right f) (right g)
      Invertible f g +++ Invertible h i = Invertible (f +++ h) (g +++ i)

instance CategoryLoop Invertible where
      loop (Invertible f g) = Invertible (loop f) (loop g)



instance (Arbitrary a, Arbitrary b, Eq a, Eq b) => Arbitrary (Invertible a b) where
instance ( Arbitrary a , Arbitrary b , Eq a , Eq b , Show a , Show b ) = > Arbitrary ( Invertible a b ) where 
      arbitrary = do
  One way to do this is to keep a map of all the values used ,
            initialAs <- sequence (repeat arbitrary)
            initialBs <- sequence (repeat arbitrary)
  Magic in the IO Monad :
# NOINLINE ref #
                ref = unsafePerformIO $ newIORef ([], initialAs, initialBs)
                             (assocs, as, bs) <- readIORef ref
                             case lookup a assocs of
                               Just b  -> return b
                               Nothing -> do
                                 let (b:bs') = bs
     " - > " P.++ P.show b
                                 writeIORef ref ((a,b):assocs, filter (/=a) as, filter (/=b) bs')
                                 return b
                addB b = do  evaluate (b==b)
                             (assocs, as, bs) <- readIORef ref
                             case lookup b (map swap assocs) of
                               Just a  -> return a
                               Nothing -> do
                                 let (a:as') = as
     " - > " P.++ P.show b
                                 writeIORef ref ((a,b):assocs, filter (/=a) as', filter (/=b) bs)
                                 return a
            return $ Invertible (unsafePerformIO . addA) (unsafePerformIO . addB)
      
      coarbitrary (Invertible f g) = coarbitrary f . coarbitrary g

instance (Eq a, Eq b, Arbitrary a, Arbitrary b, Show a, Show b) => Comparable (Invertible a b) where
      Invertible f g `equals` Invertible h i
         = property $ \x y -> f x == h x && g y == i y


 prop_invertible : : ( Arbitrary a , Arbitrary b , Eq a , Eq b ) = > Invertible a a - > Property 
 prop_invertible f = f . inverse f ` equals ` i d 

 prop_invertible : : Eq a = > Invertible a a - > a - > Bool 
 prop_invertible ( Invertible f g ) x = ( f .  ) x = = x & & ( g . f ) x = = x 

prop_invertible :: (Arbitrary a, Arbitrary b, Eq a, Eq b) => Invertible a a -> Property
prop_invertible f = f . inverse f `equals` id

prop_invertible :: Eq a => Invertible a a -> a -> Bool
prop_invertible (Invertible f g) x = (f . g) x == x && (g . f) x == x
-}