{-|
Copyright  :  (C) 2013-2016, University of Twente
                  2024,      Google LLC
License    :  BSD2 (see the file LICENSE)
Maintainer :  Christiaan Baaij <christiaan.baaij@gmail.com>
-}

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoGeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeFamilies #-}

{-# LANGUAGE Safe #-}

{-# OPTIONS_HADDOCK show-extensions #-}

module Clash.Class.Num
  ( -- * Arithmetic functions for arguments and results of different precision
    ExtendingNum (..)
    -- * Saturating arithmetic functions
  , SaturationMode (..)
  , SaturatingNum (..)
  , boundedAdd
  , boundedSub
  , boundedMul
  )
where

-- * Arithmetic functions for arguments and results of different precision

-- | Adding, subtracting, and multiplying values of two different (sub-)types.
class ExtendingNum a b where
  -- | Type of the result of the addition or subtraction
  type AResult a b
  -- | Add values of different (sub-)types, return a value of a (sub-)type
  -- that is potentially different from either argument.
  add  :: a -> b -> AResult a b
  -- | Subtract values of different (sub-)types, return a value of a (sub-)type
  -- that is potentially different from either argument.
  sub :: a -> b -> AResult a b
  -- | Type of the result of the multiplication
  type MResult a b
  -- | Multiply values of different (sub-)types, return a value of a (sub-)type
  -- that is potentially different from either argument.
  mul :: a -> b -> MResult a b

infixl 7  `mul`
infixl 6  `add`, `sub`

-- * Saturating arithmetic functions

-- | Determine how overflow and underflow are handled by the functions in
-- 'SaturatingNum'
data SaturationMode
  = SatWrap  -- ^ Wrap around on overflow and underflow
  | SatBound -- ^ Become 'maxBound' on overflow, and 'minBound' on underflow
  | SatZero  -- ^ Become @0@ on overflow and underflow
  | SatSymmetric -- ^ Become 'maxBound' on overflow, and (@'minBound' + 1@) on
                 -- underflow for signed numbers, and 'minBound' for unsigned
                 -- numbers.
  | SatError -- ^ Become an XException on overflow and underflow
  deriving (Show, Eq, Enum, Bounded)

-- | 'Num' operators in which overflow and underflow behavior can be specified
-- using 'SaturationMode'.
class (Bounded a, Num a) => SaturatingNum a where
  -- | Addition with parameterizable over- and underflow behavior
  satAdd :: SaturationMode -> a -> a -> a
  -- | Subtraction with parameterizable over- and underflow behavior
  satSub  :: SaturationMode -> a -> a -> a
  -- | Multiplication with parameterizable over- and underflow behavior
  satMul :: SaturationMode -> a -> a -> a
  -- | Get successor of (or in other words, add 1 to) given number
  satSucc :: SaturationMode -> a -> a
  -- Default method suitable for types that can represent the number 1
  satSucc s n = satAdd s n 1
  {-# INLINE satSucc #-}
  -- | Get predecessor of (or in other words, subtract 1 from) given number
  satPred :: SaturationMode -> a -> a
  -- Default method suitable for types that can represent the number 1
  satPred s n = satSub s n 1
  {-# INLINE satPred #-}

infixl 7  `satMul`
infixl 6  `satAdd`, `satSub`

-- | Addition that clips to 'maxBound' on overflow, and 'minBound' on underflow
boundedAdd :: SaturatingNum a => a -> a -> a
boundedAdd = satAdd SatBound
{-# INLINE boundedAdd #-}
infixl 6  `boundedAdd`

-- | Subtraction that clips to 'maxBound' on overflow, and 'minBound' on
-- underflow
boundedSub  :: SaturatingNum a => a -> a -> a
boundedSub = satSub SatBound
{-# INLINE boundedSub #-}
infixl 6 `boundedSub`

-- | Multiplication that clips to 'maxBound' on overflow, and 'minBound' on
-- underflow
boundedMul :: SaturatingNum a => a -> a -> a
boundedMul = satMul SatBound
{-# INLINE boundedMul #-}
infixl 7  `boundedMul`
