{-# OPTIONS_GHC -Wall #-}
----------------------------------------------------------------------
-- |
-- Module      :  Data.AddBounds
-- Copyright   :  (c) Conal Elliott 2008
-- License     :  BSD3
-- 
-- Maintainer  :  conal@conal.net
-- Stability   :  experimental
-- 
-- Add bounds to an ordered type
----------------------------------------------------------------------

module Data.AddBounds (AddBounds(..)) where

import Control.Applicative (pure,(<$>))

-- Testing
import Test.QuickCheck
import Test.QuickCheck.Checkers


-- | Wrap a type into one having new least and greatest elements,
-- preserving the existing ordering.
data AddBounds a = MinBound | NoBound a | MaxBound
	deriving (Eq {-, Ord-}, Read, Show)

instance Bounded (AddBounds a) where
	minBound = MinBound
	maxBound = MaxBound


-- Normally, I'd derive 'Ord' as well, but there's a sticky point.  The
-- derived instance uses the default definition of 'min', which is uses
-- '(<=)' and thus cannot exploit any partial information.  So, define our
-- own 'min' in terms of 'min' on @a@.
-- Examples:
--   (NoBound undefined) `min` (NoBound undefined) can return (NoBound _|_)
--   using this definition, but will not produce any output using the
--   default min.
--   
--   (NoBound a) `min` (NoBound b) can return partial information from
--   a `min` b while the default implementation cannot.

instance Ord a => Ord (AddBounds a) where
  MinBound  <= _         = True
  NoBound _ <= MinBound  = False
  NoBound a <= NoBound b = a <= b
  NoBound _ <= MaxBound  = True
  MaxBound  <= MaxBound  = True
  MaxBound  <= _         = False        -- given previous 
  
  MinBound  `min` _         = MinBound
  _         `min` MinBound  = MinBound
  NoBound a `min` NoBound b = NoBound (a `min` b)
  u         `min` MaxBound  = u
  MaxBound  `min` v         = v
  
  MinBound  `max` v         = v
  u         `max` MinBound  = u
  NoBound a `max` NoBound b = NoBound (a `max` b)
  _         `max` MaxBound  = MaxBound
  MaxBound  `max` _         = MaxBound


instance Arbitrary a => Arbitrary (AddBounds a) where
  arbitrary = frequency [ (1 ,pure MinBound)
                        , (10, NoBound <$> arbitrary)
                        , (1 ,pure MaxBound) ]
  coarbitrary MinBound    = variant 0
  coarbitrary (NoBound a) = variant 1 . coarbitrary a
  coarbitrary MaxBound    = variant 2

instance (EqProp a, Eq a) => EqProp (AddBounds a) where
  NoBound a =-= NoBound b = a =-= b
  u =-= v = u `eq` v