{-# LANGUAGE DataKinds #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE Unsafe #-} {-# OPTIONS_HADDOCK show-extensions #-} {-| Copyright : (C) 2013-2015, University of Twente License : BSD2 (see the file LICENSE) Maintainer : Christiaan Baaij -} module CLaSH.Sized.Internal.Index ( -- * Datatypes Index (..) -- * Type classes -- ** BitConvert , pack# , unpack# -- ** Eq , eq# , neq# -- ** Ord , lt# , ge# , gt# , le# -- ** Enum (not synthesisable) , enumFrom# , enumFromThen# , enumFromTo# , enumFromThenTo# -- ** Bounded , maxBound# -- ** Num , (+#) , (-#) , (*#) , fromInteger# -- ** ExtendingNum , plus# , minus# , times# -- ** Integral , quot# , rem# , toInteger# -- ** Resize , resize# ) where import Data.Default (Default (..)) import Language.Haskell.TH (TypeQ, appT, conT, litT, numTyLit, sigE) import Language.Haskell.TH.Syntax (Lift(..)) import GHC.TypeLits (KnownNat, Nat, type (+), type (-), type (*), natVal) import GHC.TypeLits.Extra (CLog) import Test.QuickCheck.Arbitrary (Arbitrary (..), CoArbitrary (..), arbitrarySizedBoundedIntegral, coarbitraryIntegral, shrinkIntegral) import CLaSH.Class.BitPack (BitPack (..)) import CLaSH.Class.Num (ExtendingNum (..)) import CLaSH.Class.Resize (Resize (..)) import {-# SOURCE #-} CLaSH.Sized.Internal.BitVector (BitVector (..)) -- | Arbitrary-bounded unsigned integer represented by @ceil(log_2(n))@ bits. -- -- Given an upper bound @n@, an 'Index' @n@ number has a range of: [0 .. @n@-1] -- -- >>> maxBound :: Index 8 -- 7 -- >>> minBound :: Index 8 -- 0 -- >>> 1 + 2 :: Index 8 -- 3 -- >>> 2 + 6 :: Index 8 -- *** Exception: CLaSH.Sized.Index: result 8 is out of bounds: [0..7] -- >>> 1 - 3 :: Index 8 -- *** Exception: CLaSH.Sized.Index: result -2 is out of bounds: [0..7] -- >>> 2 * 3 :: Index 8 -- 6 -- >>> 2 * 4 :: Index 8 -- *** Exception: CLaSH.Sized.Index: result 8 is out of bounds: [0..7] newtype Index (n :: Nat) = -- | The constructor, 'I', and the field, 'unsafeToInteger', are not -- synthesisable. I { unsafeToInteger :: Integer } instance KnownNat n => BitPack (Index n) where type BitSize (Index n) = CLog 2 n pack = pack# unpack = unpack# {-# NOINLINE pack# #-} pack# :: Index n -> BitVector (CLog 2 n) pack# (I i) = BV i {-# NOINLINE unpack# #-} unpack# :: KnownNat n => BitVector (CLog 2 n) -> Index n unpack# (BV i) = fromInteger_INLINE i instance Eq (Index n) where (==) = eq# (/=) = neq# {-# NOINLINE eq# #-} eq# :: (Index n) -> (Index n) -> Bool (I n) `eq#` (I m) = n == m {-# NOINLINE neq# #-} neq# :: (Index n) -> (Index n) -> Bool (I n) `neq#` (I m) = n /= m instance Ord (Index n) where (<) = lt# (>=) = ge# (>) = gt# (<=) = le# lt#,ge#,gt#,le# :: Index n -> Index n -> Bool {-# NOINLINE lt# #-} lt# (I n) (I m) = n < m {-# NOINLINE ge# #-} ge# (I n) (I m) = n >= m {-# NOINLINE gt# #-} gt# (I n) (I m) = n > m {-# NOINLINE le# #-} le# (I n) (I m) = n <= m -- | The functions: 'enumFrom', 'enumFromThen', 'enumFromTo', and -- 'enumFromThenTo', are not synthesisable. instance KnownNat n => Enum (Index n) where succ = (+# fromInteger# 1) pred = (-# fromInteger# 1) toEnum = fromInteger# . toInteger fromEnum = fromEnum . toInteger# enumFrom = enumFrom# enumFromThen = enumFromThen# enumFromTo = enumFromTo# enumFromThenTo = enumFromThenTo# {-# NOINLINE enumFrom# #-} {-# NOINLINE enumFromThen# #-} {-# NOINLINE enumFromTo# #-} {-# NOINLINE enumFromThenTo# #-} enumFrom# :: KnownNat n => Index n -> [Index n] enumFromThen# :: KnownNat n => Index n -> Index n -> [Index n] enumFromTo# :: KnownNat n => Index n -> Index n -> [Index n] enumFromThenTo# :: KnownNat n => Index n -> Index n -> Index n -> [Index n] enumFrom# x = map toEnum [fromEnum x ..] enumFromThen# x y = map toEnum [fromEnum x, fromEnum y ..] enumFromTo# x y = map toEnum [fromEnum x .. fromEnum y] enumFromThenTo# x1 x2 y = map toEnum [fromEnum x1, fromEnum x2 .. fromEnum y] instance KnownNat n => Bounded (Index n) where minBound = fromInteger# 0 maxBound = maxBound# {-# NOINLINE maxBound# #-} maxBound# :: KnownNat n => Index n maxBound# = let res = I (natVal res - 1) in res -- | Operators report an error on overflow and underflow instance KnownNat n => Num (Index n) where (+) = (+#) (-) = (-#) (*) = (*#) negate = (maxBound# -#) abs = id signum i = if i == 0 then 0 else 1 fromInteger = fromInteger# (+#),(-#),(*#) :: KnownNat n => Index n -> Index n -> Index n {-# NOINLINE (+#) #-} (+#) (I a) (I b) = fromInteger_INLINE $ a + b {-# NOINLINE (-#) #-} (-#) (I a) (I b) = fromInteger_INLINE $ a - b {-# NOINLINE (*#) #-} (*#) (I a) (I b) = fromInteger_INLINE $ a * b fromInteger#,fromInteger_INLINE :: KnownNat n => Integer -> Index n {-# NOINLINE fromInteger# #-} fromInteger# = fromInteger_INLINE {-# INLINE fromInteger_INLINE #-} fromInteger_INLINE i = let bound = natVal res i' = i `mod` bound err = error ("CLaSH.Sized.Index: result " ++ show i ++ " is out of bounds: [0.." ++ show (bound - 1) ++ "]") res = if i' /= i then err else I i in res instance ExtendingNum (Index m) (Index n) where type AResult (Index m) (Index n) = Index (m + n - 1) plus = plus# minus = minus# type MResult (Index m) (Index n) = Index (((m - 1) * (n - 1)) + 1) times = times# plus#, minus# :: Index m -> Index n -> Index (m + n - 1) {-# NOINLINE plus# #-} plus# (I a) (I b) = I (a + b) {-# NOINLINE minus# #-} minus# (I a) (I b) = let z = a - b err = error ("CLaSH.Sized.Index.minus: result " ++ show z ++ " is smaller than 0") res = if z < 0 then err else I z in res {-# NOINLINE times# #-} times# :: Index m -> Index n -> Index (((m - 1) * (n - 1)) + 1) times# (I a) (I b) = I (a * b) instance KnownNat n => Real (Index n) where toRational = toRational . toInteger# instance KnownNat n => Integral (Index n) where quot = quot# rem = rem# div = quot# mod = rem# quotRem n d = (n `quot#` d,n `rem#` d) divMod n d = (n `quot#` d,n `rem#` d) toInteger = toInteger# quot#,rem# :: Index n -> Index n -> Index n {-# NOINLINE quot# #-} (I a) `quot#` (I b) = I (a `div` b) {-# NOINLINE rem# #-} (I a) `rem#` (I b) = I (a `rem` b) {-# NOINLINE toInteger# #-} toInteger# :: Index n -> Integer toInteger# (I n) = n instance Resize Index where resize = resize# zeroExtend = resize# signExtend = resize# truncateB = resize# resize# :: KnownNat m => Index n -> Index m resize# (I i) = fromInteger_INLINE i {-# NOINLINE resize# #-} instance KnownNat n => Lift (Index n) where lift u@(I i) = sigE [| fromInteger# i |] (decIndex (natVal u)) decIndex :: Integer -> TypeQ decIndex n = appT (conT ''Index) (litT $ numTyLit n) instance Show (Index n) where show (I n) = show n instance KnownNat n => Default (Index n) where def = fromInteger# 0 instance KnownNat n => Arbitrary (Index n) where arbitrary = arbitrarySizedBoundedIntegral shrink = shrinkIntegral instance KnownNat n => CoArbitrary (Index n) where coarbitrary = coarbitraryIntegral