    {-|
    Copyright  :  (C) 2019, Myrtle Software Ltd
                      2024, QBayLogic B.V.
    License    :  BSD2 (see the file LICENSE)
    Maintainer :  QBayLogic B.V. <devops@qaylogic.com>
    -}

    {-# LANGUAGE CPP #-}
    {-# LANGUAGE TypeFamilies #-}
    {-# LANGUAGE UndecidableInstances #-}

    {-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}

    module Clash.Class.Exp
      ( Exp
      , ExpResult
      , (^)

      -- | * Internal
      , expIndex#
      , expSigned#
      , expUnsigned#
      ) where

    import qualified Prelude                       as P
    import           Prelude                       hiding ((^))

    import           Clash.Annotations.Primitive   (hasBlackBox)
    import           Clash.Promoted.Nat            (SNat(..), snatToInteger)
    import           Clash.Sized.Internal.Index    (Index)
    import           Clash.Sized.Internal.Signed   (Signed)
    import           Clash.Sized.Internal.Unsigned (Unsigned)

    import           GHC.TypeLits
      (KnownNat, Nat, type (^), type (*))
    import           GHC.TypeLits.Extra            (Max)

    -- | Type class implementing exponentiation with explicitly resizing results.
    class Exp a where
      type ExpResult a (n :: Nat)

      -- | Exponentiation with known exponent.
      (^)
        :: a
        -- ^ Base
        -> SNat n
        -- ^ Exponent
        -> ExpResult a n
        -- ^ Resized result, guaranteed to not have overflown

    infixr 8 ^

    instance KnownNat m => Exp (Index m) where
      type ExpResult (Index m) n = Index (Max 2 (m ^ n))

      ^ :: forall (n :: Nat). Index m -> SNat n -> ExpResult (Index m) n
    (^) = Index m -> SNat n -> Index (Max 2 (m ^ n))
    Index m -> SNat n -> ExpResult (Index m) n
    forall (m :: Nat) (n :: Nat).
    KnownNat m =>
    Index m -> SNat n -> Index (Max 2 (m ^ n))
    expIndex#
      {-# INLINE (^) #-}

    instance KnownNat m => Exp (Signed m) where
      type ExpResult (Signed m) n = Signed (Max 2 (m * n))

      ^ :: forall (n :: Nat). Signed m -> SNat n -> ExpResult (Signed m) n
    (^) = Signed m -> SNat n -> Signed (Max 2 (m * n))
    Signed m -> SNat n -> ExpResult (Signed m) n
    forall (m :: Nat) (n :: Nat).
    KnownNat m =>
    Signed m -> SNat n -> Signed (Max 2 (m * n))
    expSigned#
      {-# INLINE (^) #-}

    instance KnownNat m => Exp (Unsigned m) where
      type ExpResult (Unsigned m) n = Unsigned (Max 1 (m * n))

      ^ :: forall (n :: Nat). Unsigned m -> SNat n -> ExpResult (Unsigned m) n
    (^) = Unsigned m -> SNat n -> Unsigned (Max 1 (m * n))
    Unsigned m -> SNat n -> ExpResult (Unsigned m) n
    forall (m :: Nat) (n :: Nat).
    KnownNat m =>
    Unsigned m -> SNat n -> Unsigned (Max 1 (m * n))
    expUnsigned#
      {-# INLINE (^) #-}

    expIndex#
      :: KnownNat m
      => Index m
      -> SNat n
      -> Index (Max 2 (m ^ n))
    expIndex# :: forall (m :: Nat) (n :: Nat).
    KnownNat m =>
    Index m -> SNat n -> Index (Max 2 (m ^ n))
    expIndex# Index m
    b e :: SNat n
    e@SNat n
    SNat =
      Integer -> Index (Max 2 (m ^ n))
    forall a. Num a => Integer -> a
    fromInteger (Index m -> Integer
    forall a. Integral a => a -> Integer
    toInteger Index m
    b Integer -> Integer -> Integer
    forall a b. (Num a, Integral b) => a -> b -> a
    P.^ SNat n -> Integer
    forall (n :: Nat). SNat n -> Integer
    snatToInteger SNat n
    e)
    -- See: https://github.com/clash-lang/clash-compiler/pull/2511
    {-# CLASH_OPAQUE expIndex# #-}
    {-# ANN expIndex# hasBlackBox #-}

    expSigned#
      :: KnownNat m
      => Signed m
      -> SNat n
      -> Signed (Max 2 (m * n))
    expSigned# :: forall (m :: Nat) (n :: Nat).
    KnownNat m =>
    Signed m -> SNat n -> Signed (Max 2 (m * n))
    expSigned# Signed m
    b e :: SNat n
    e@SNat n
    SNat =
      Integer -> Signed (Max 2 (m * n))
    forall a. Num a => Integer -> a
    fromInteger (Signed m -> Integer
    forall a. Integral a => a -> Integer
    toInteger Signed m
    b Integer -> Integer -> Integer
    forall a b. (Num a, Integral b) => a -> b -> a
    P.^ SNat n -> Integer
    forall (n :: Nat). SNat n -> Integer
    snatToInteger SNat n
    e)
    -- See: https://github.com/clash-lang/clash-compiler/pull/2511
    {-# CLASH_OPAQUE expSigned# #-}
    {-# ANN expSigned# hasBlackBox #-}

    expUnsigned#
      :: KnownNat m
      => Unsigned m
      -> SNat n
      -> Unsigned (Max 1 (m * n))
    expUnsigned# :: forall (m :: Nat) (n :: Nat).
    KnownNat m =>
    Unsigned m -> SNat n -> Unsigned (Max 1 (m * n))
    expUnsigned# Unsigned m
    b e :: SNat n
    e@SNat n
    SNat =
      Integer -> Unsigned (Max 1 (m * n))
    forall a. Num a => Integer -> a
    fromInteger (Unsigned m -> Integer
    forall a. Integral a => a -> Integer
    toInteger Unsigned m
    b Integer -> Integer -> Integer
    forall a b. (Num a, Integral b) => a -> b -> a
    P.^ SNat n -> Integer
    forall (n :: Nat). SNat n -> Integer
    snatToInteger SNat n
    e)
    -- See: https://github.com/clash-lang/clash-compiler/pull/2511
    {-# CLASH_OPAQUE expUnsigned# #-}
    {-# ANN expUnsigned# hasBlackBox #-}