{-# LANGUAGE ConstraintKinds   #-}
{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TemplateHaskell   #-}
{-# LANGUAGE TypeApplications  #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- |
-- Module      : Data.Array.Accelerate.Classes.Bounded
-- Copyright   : [2016..2020] The Accelerate Team
-- License     : BSD3
--
-- Maintainer  : Trevor L. McDonell <trevor.mcdonell@gmail.com>
-- Stability   : experimental
-- Portability : non-portable (GHC extensions)
--

module Data.Array.Accelerate.Classes.Bounded (

  Bounded,
  P.minBound, P.maxBound,

) where

import Data.Array.Accelerate.Array.Data
import Data.Array.Accelerate.Pattern
import Data.Array.Accelerate.Smart
import Data.Array.Accelerate.Sugar.Elt
import Data.Array.Accelerate.Type

import Prelude                                                      ( ($), (<$>), Num(..), Char, Bool, show, concat, map, mapM )
import Language.Haskell.TH                                          hiding ( Exp )
import Language.Haskell.TH.Extra
import qualified Prelude                                            as P


-- | Name the upper and lower limits of a type. Types which are not totally
-- ordered may still have upper and lower bounds.
--
type Bounded a = (Elt a, P.Bounded (Exp a))


instance P.Bounded (Exp ()) where
  minBound :: Exp ()
minBound = () -> Exp ()
forall e. (HasCallStack, Elt e) => e -> Exp e
constant ()
  maxBound :: Exp ()
maxBound = () -> Exp ()
forall e. (HasCallStack, Elt e) => e -> Exp e
constant ()

instance P.Bounded (Exp Int) where
  minBound :: Exp Int
minBound = Exp Int
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound
  maxBound :: Exp Int
maxBound = Exp Int
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound

instance P.Bounded (Exp Int8) where
  minBound :: Exp Int8
minBound = Exp Int8
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound
  maxBound :: Exp Int8
maxBound = Exp Int8
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound

instance P.Bounded (Exp Int16) where
  minBound :: Exp Int16
minBound = Exp Int16
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound
  maxBound :: Exp Int16
maxBound = Exp Int16
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound

instance P.Bounded (Exp Int32) where
  minBound :: Exp Int32
minBound = Exp Int32
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound
  maxBound :: Exp Int32
maxBound = Exp Int32
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound

instance P.Bounded (Exp Int64) where
  minBound :: Exp Int64
minBound = Exp Int64
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound
  maxBound :: Exp Int64
maxBound = Exp Int64
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound

instance P.Bounded (Exp Word) where
  minBound :: Exp Word
minBound = Exp Word
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound
  maxBound :: Exp Word
maxBound = Exp Word
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound

instance P.Bounded (Exp Word8) where
  minBound :: Exp Word8
minBound = Exp Word8
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound
  maxBound :: Exp Word8
maxBound = Exp Word8
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound

instance P.Bounded (Exp Word16) where
  minBound :: Exp Word16
minBound = Exp Word16
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound
  maxBound :: Exp Word16
maxBound = Exp Word16
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound

instance P.Bounded (Exp Word32) where
  minBound :: Exp Word32
minBound = Exp Word32
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound
  maxBound :: Exp Word32
maxBound = Exp Word32
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound

instance P.Bounded (Exp Word64) where
  minBound :: Exp Word64
minBound = Exp Word64
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound
  maxBound :: Exp Word64
maxBound = Exp Word64
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound

instance P.Bounded (Exp CShort) where
  minBound :: Exp CShort
minBound = Exp Int16 -> Exp CShort
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Int16, IsBounded (EltR Int16)) => Exp Int16
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound @Int16)
  maxBound :: Exp CShort
maxBound = Exp Int16 -> Exp CShort
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Int16, IsBounded (EltR Int16)) => Exp Int16
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound @Int16)

instance P.Bounded (Exp CUShort) where
  minBound :: Exp CUShort
minBound = Exp Word16 -> Exp CUShort
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Word16, IsBounded (EltR Word16)) => Exp Word16
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound @Word16)
  maxBound :: Exp CUShort
maxBound = Exp Word16 -> Exp CUShort
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Word16, IsBounded (EltR Word16)) => Exp Word16
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound @Word16)

instance P.Bounded (Exp CInt) where
  minBound :: Exp CInt
minBound = Exp Int32 -> Exp CInt
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Int32, IsBounded (EltR Int32)) => Exp Int32
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound @Int32)
  maxBound :: Exp CInt
maxBound = Exp Int32 -> Exp CInt
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Int32, IsBounded (EltR Int32)) => Exp Int32
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound @Int32)

instance P.Bounded (Exp CUInt) where
  minBound :: Exp CUInt
minBound = Exp Word32 -> Exp CUInt
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Word32, IsBounded (EltR Word32)) => Exp Word32
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound @Word32)
  maxBound :: Exp CUInt
maxBound = Exp Word32 -> Exp CUInt
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Word32, IsBounded (EltR Word32)) => Exp Word32
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound @Word32)

instance P.Bounded (Exp CLong) where
  minBound :: Exp CLong
minBound = Exp Int64 -> Exp CLong
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Int64, IsBounded (EltR Int64)) => Exp Int64
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound @HTYPE_CLONG)
  maxBound :: Exp CLong
maxBound = Exp Int64 -> Exp CLong
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Int64, IsBounded (EltR Int64)) => Exp Int64
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound @HTYPE_CLONG)

instance P.Bounded (Exp CULong) where
  minBound :: Exp CULong
minBound = Exp Word64 -> Exp CULong
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Word64, IsBounded (EltR Word64)) => Exp Word64
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound @HTYPE_CULONG)
  maxBound :: Exp CULong
maxBound = Exp Word64 -> Exp CULong
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Word64, IsBounded (EltR Word64)) => Exp Word64
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound @HTYPE_CULONG)

instance P.Bounded (Exp CLLong) where
  minBound :: Exp CLLong
minBound = Exp Int64 -> Exp CLLong
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Int64, IsBounded (EltR Int64)) => Exp Int64
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound @Int64)
  maxBound :: Exp CLLong
maxBound = Exp Int64 -> Exp CLLong
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Int64, IsBounded (EltR Int64)) => Exp Int64
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound @Int64)

instance P.Bounded (Exp CULLong) where
  minBound :: Exp CULLong
minBound = Exp Word64 -> Exp CULLong
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Word64, IsBounded (EltR Word64)) => Exp Word64
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound @Word64)
  maxBound :: Exp CULLong
maxBound = Exp Word64 -> Exp CULLong
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Word64, IsBounded (EltR Word64)) => Exp Word64
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound @Word64)

instance P.Bounded (Exp Bool) where
  minBound :: Exp Bool
minBound = Bool -> Exp Bool
forall e. (HasCallStack, Elt e) => e -> Exp e
constant Bool
forall a. Bounded a => a
P.minBound
  maxBound :: Exp Bool
maxBound = Bool -> Exp Bool
forall e. (HasCallStack, Elt e) => e -> Exp e
constant Bool
forall a. Bounded a => a
P.maxBound

instance P.Bounded (Exp Char) where
  minBound :: Exp Char
minBound = Exp Char
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound
  maxBound :: Exp Char
maxBound = Exp Char
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound

instance P.Bounded (Exp CChar) where
  minBound :: Exp CChar
minBound = Exp Int8 -> Exp CChar
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Int8, IsBounded (EltR Int8)) => Exp Int8
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound @HTYPE_CCHAR)
  maxBound :: Exp CChar
maxBound = Exp Int8 -> Exp CChar
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Int8, IsBounded (EltR Int8)) => Exp Int8
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound @HTYPE_CCHAR)

instance P.Bounded (Exp CSChar) where
  minBound :: Exp CSChar
minBound = Exp Int8 -> Exp CSChar
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Int8, IsBounded (EltR Int8)) => Exp Int8
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound @Int8)
  maxBound :: Exp CSChar
maxBound = Exp Int8 -> Exp CSChar
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Int8, IsBounded (EltR Int8)) => Exp Int8
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound @Int8)

instance P.Bounded (Exp CUChar) where
  minBound :: Exp CUChar
minBound = Exp Word8 -> Exp CUChar
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Word8, IsBounded (EltR Word8)) => Exp Word8
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMinBound @Word8)
  maxBound :: Exp CUChar
maxBound = Exp Word8 -> Exp CUChar
forall b a.
(Elt a, Elt b, IsScalar (EltR a), IsScalar (EltR b),
 BitSizeEq (EltR a) (EltR b)) =>
Exp a -> Exp b
mkBitcast ((Elt Word8, IsBounded (EltR Word8)) => Exp Word8
forall t. (Elt t, IsBounded (EltR t)) => Exp t
mkMaxBound @Word8)

$(runQ $ do
    let
        mkInstance :: Int -> Q [Dec]
        mkInstance n =
          let
              xs      = [ mkName ('x':show i) | i <- [0 .. n-1] ]
              cst     = tupT (map (\x -> [t| Bounded $(varT x) |]) xs)
              res     = tupT (map varT xs)
              app x   = appsE (conE (mkName ('T':show n)) : P.replicate n x)
          in
          [d| instance $cst => P.Bounded (Exp $res) where
                minBound = $(app [| P.minBound |])
                maxBound = $(app [| P.maxBound |])
            |]
    --
    concat <$> mapM mkInstance [2..16]
 )