{-# LANGUAGE AllowAmbiguousTypes   #-}
{-# LANGUAGE DefaultSignatures     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TypeSynonymInstances  #-}

{- | Safe numeric operations.

We take a fairly conservative approach here - this module is an extension not
a replacement for "Prelude". Our suffix naming conventions are as follows:

- @\@@ or @W@ -- wrap - same as "Prelude", but confirms explicitly to the
  reader that you thought about the issue
- @%@ or @E@ -- explicit error (@'Either' 'ArithException'@)
- @:@ or @S@ -- saturate at 'minBound' or 'maxBound'
- @!@ or @X@ -- runtime exception. This is suitable only for trusted inputs,
  since otherwise you allow the attacker to crash your code arbitrarily.

Currently we provide replacements for:

- conversion functions 'fromIntegral' and 'fromInteger'; instead use

    - 'ex' for cost-free conversions to a bigger or same-sized type, or
    - 'ctW', 'ctE', 'ctS', or 'ctX' for checked conversions to a smaller type

- arithmetic functions '+', '-', '*', '^'; instead use

    - '+@', '+%', '+:', '+!', etc

- division functions 'div', 'mod', 'divMod', 'quot', 'rem', 'quotRem'; instead use

    - 'divE', 'divX', etc

When using this module, you might also like to ban the unsafe functions from
your codebase, e.g. via hlint:

@
- functions:
  - {name: [fromIntegral, fromInteger, +, \'-\', \'*\', ^], within: [], message: "Use safe versions from Safe.Numeric"}
  - {name: [div, mod, divMod, quot, rem, quotRem], within: [], message: "Use safe versions from Safe.Numeric"}
@

-}
module Safe.Numeric
  (
  -- * Conversions
    NumExpand(..)
  , NumConvert(..)
  -- ** Summary of instances
  -- $instances
  , Word29_
  , Int29_
  -- * Arithmetic
  , (+@)
  , (+%)
  , (+:)
  , (+!)
  , (-@)
  , (-%)
  , (-:)
  , (-!)
  , (*@)
  , (*%)
  , (*:)
  , (*!)
  , (^@)
  , (^%)
  , (^:)
  , (^!)
  -- * Division
  , DivResult
  , divE
  , divX
  , modE
  , modX
  , divModE
  , divModX
  , quotE
  , quotX
  , remE
  , remX
  , quotRemE
  , quotRemX
  ) where

-- external
import           Control.Exception (ArithException (..))
import           Data.Int
import           Data.WideWord
import           Data.Word
import           Safe.Partial      (Partial)


class NumExpand b a where
  {- | Safely expand type @b@ into type @a@, with no runtime bounds checking.

  a.k.a. "'fromIntegral' hurts my fingers and my eyes"

  The value is statically guaranteed to remain the same relative to 0 in both
  directions, i.e. not overflow or underflow, without any runtime checks.
  -}
  ex :: b -> a
  default ex :: (Num a, Integral b) => b -> a
  ex = b -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral
  {-# INLINE ex #-}

checkE
  :: forall a . (Integral a, Bounded a) => Integer -> Either ArithException a
checkE :: Integer -> Either ArithException a
checkE Integer
v | Integer
v Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
< a -> Integer
forall a. Integral a => a -> Integer
toInteger (a
forall a. Bounded a => a
minBound :: a) = ArithException -> Either ArithException a
forall a b. a -> Either a b
Left ArithException
Underflow
         | Integer
v Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
> a -> Integer
forall a. Integral a => a -> Integer
toInteger (a
forall a. Bounded a => a
maxBound :: a) = ArithException -> Either ArithException a
forall a b. a -> Either a b
Left ArithException
Overflow
         | Bool
otherwise                     = a -> Either ArithException a
forall a b. b -> Either a b
Right (Integer -> a
forall a. Num a => Integer -> a
fromInteger Integer
v)
{-# INLINE checkE #-}

checkS :: forall a . (Integral a, Bounded a) => Integer -> a
checkS :: Integer -> a
checkS Integer
v | Integer
v Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
< a -> Integer
forall a. Integral a => a -> Integer
toInteger (a
forall a. Bounded a => a
minBound :: a) = a
forall a. Bounded a => a
minBound
         | Integer
v Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
> a -> Integer
forall a. Integral a => a -> Integer
toInteger (a
forall a. Bounded a => a
maxBound :: a) = a
forall a. Bounded a => a
maxBound
         | Bool
otherwise                     = Integer -> a
forall a. Num a => Integer -> a
fromInteger Integer
v
{-# INLINE checkS #-}

checkX :: forall a . (Integral a, Bounded a) => Partial => Integer -> a
checkX :: Integer -> a
checkX Integer
v | Integer
v Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
< a -> Integer
forall a. Integral a => a -> Integer
toInteger (a
forall a. Bounded a => a
minBound :: a) = [Char] -> a
forall a. HasCallStack => [Char] -> a
error [Char]
"Underflow"
         | Integer
v Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
> a -> Integer
forall a. Integral a => a -> Integer
toInteger (a
forall a. Bounded a => a
maxBound :: a) = [Char] -> a
forall a. HasCallStack => [Char] -> a
error [Char]
"Overflow"
         | Bool
otherwise                     = Integer -> a
forall a. Num a => Integer -> a
fromInteger Integer
v
{-# INLINE checkX #-}

-- | Convert from a type into a smaller type
class NumConvert b a where

  -- | Wrap around if the input is out-of-bounds.
  ctW :: b -> a
  default ctW :: (Num a, Integral b) => b -> a
  ctW b
b = b -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral b
b
  {-# INLINE ctW #-}

  -- | Explicit error if the input is out-of-bounds.
  ctE :: b -> Either ArithException a
  default ctE :: (Integral a, Bounded a, Integral b) => b -> Either ArithException a
  ctE = Integer -> Either ArithException a
forall a.
(Integral a, Bounded a) =>
Integer -> Either ArithException a
checkE (Integer -> Either ArithException a)
-> (b -> Integer) -> b -> Either ArithException a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. b -> Integer
forall a. Integral a => a -> Integer
toInteger
  {-# INLINE ctE #-}

  -- | Output 'minBound' if the input is too small, or 'maxBound' if too large.
  ctS :: b -> a
  default ctS :: (Integral a, Bounded a, Integral b) => b -> a
  ctS = Integer -> a
forall a. (Integral a, Bounded a) => Integer -> a
checkS (Integer -> a) -> (b -> Integer) -> b -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. b -> Integer
forall a. Integral a => a -> Integer
toInteger
  {-# INLINE ctS #-}

  -- | Runtime (async) exception if the input is out-of-bounds.
  ctX :: Partial => b -> a
  default ctX :: (Integral a, Bounded a, Integral b) => Partial => b -> a
  ctX = Integer -> a
forall a. (Integral a, Bounded a, HasCallStack) => Integer -> a
checkX (Integer -> a) -> (b -> Integer) -> b -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. b -> Integer
forall a. Integral a => a -> Integer
toInteger
  {-# INLINE ctX #-}

{- | Type alias for 'Word' that explicitly states its lower-bound size. -}
type Word29_ = Word

{- | Type alias for 'Int' that explicitly states its lower-bound size. -}
type Int29_ = Int

{- $instances

@
Fr\\To W8    W16   W29_  W32   W64   W128  W256  I8    I16   I29_  I32   I64   I128  I256  Itgr
W8    X     X     X     X     X     X     X           X     X     X     X     X     X     X
W16         X     X     X     X     X     X                 X     X     X     X     X     X
W29_              X                                                                       X
W32                     X     X     X     X                             X     X     X     X
W64                           X     X     X                                   X     X     X
W128                                X     X                                         X     X
W256                                      X                                               X
I8                                              X     X     X     X     X     X     X     X
I16                                                   X     X     X     X     X     X     X
I29_                                                        X                             X
I32                                                               X     X     X     X     X
I64                                                                     X     X     X     X
I128                                                                          X     X     X
I256                                                                                X     X
Itgr                                                                                      X
@

In the above table, X means 'NumExpand', empty means 'NumConvert'.

Safe, free 'NumExpand' from @W29_@ and @I29_@ to other bounded types are not
guaranteed since there is no specified upper bound on the sizes of the former.

Generally, we cannot have @NumExpand a b@ /and/ @NumExpand b a@ unless @b = a@.
-}

-- note: wide-word does not yet define Int256, so references to it below are
-- commented out

{- | -}
instance NumExpand Word8 Word8
instance NumExpand Word8 Word16
instance NumExpand Word8 Word29_
instance NumExpand Word8 Word32
instance NumExpand Word8 Word64
instance NumExpand Word8 Word128
instance NumExpand Word8 Word256
instance NumConvert Word8 Int8
instance NumExpand Word8 Int16
instance NumExpand Word8 Int29_
instance NumExpand Word8 Int32
instance NumExpand Word8 Int64
instance NumExpand Word8 Int128
--instance NumExpand Word8 Int256
instance NumExpand Word8 Integer

instance NumConvert Word16 Word8
instance NumExpand Word16 Word16
instance NumExpand Word16 Word29_
instance NumExpand Word16 Word32
instance NumExpand Word16 Word64
instance NumExpand Word16 Word128
instance NumExpand Word16 Word256
instance NumConvert Word16 Int8
instance NumConvert Word16 Int16
instance NumExpand Word16 Int29_
instance NumExpand Word16 Int32
instance NumExpand Word16 Int64
instance NumExpand Word16 Int128
--instance NumExpand Word16 Int256
instance NumExpand Word16 Integer

instance NumConvert Word29_ Word8
instance NumConvert Word29_ Word16
instance NumExpand Word29_ Word29_
instance NumConvert Word29_ Word32
instance NumConvert Word29_ Word64
instance NumConvert Word29_ Word128
instance NumConvert Word29_ Word256
instance NumConvert Word29_ Int8
instance NumConvert Word29_ Int16
instance NumConvert Word29_ Int29_
instance NumConvert Word29_ Int32
instance NumConvert Word29_ Int64
instance NumConvert Word29_ Int128
--instance NumConvert Word29_ Int256
instance NumExpand Word29_ Integer

instance NumConvert Word32 Word8
instance NumConvert Word32 Word16
instance NumConvert Word32 Word29_
instance NumExpand Word32 Word32
instance NumExpand Word32 Word64
instance NumExpand Word32 Word128
instance NumExpand Word32 Word256
instance NumConvert Word32 Int8
instance NumConvert Word32 Int16
instance NumConvert Word32 Int29_
instance NumConvert Word32 Int32
instance NumExpand Word32 Int64
instance NumExpand Word32 Int128
--instance NumExpand Word32 Int256
instance NumExpand Word32 Integer

instance NumConvert Word64 Word8
instance NumConvert Word64 Word16
instance NumConvert Word64 Word29_
instance NumConvert Word64 Word32
instance NumExpand Word64 Word64
instance NumExpand Word64 Word128
instance NumExpand Word64 Word256
instance NumConvert Word64 Int8
instance NumConvert Word64 Int16
instance NumConvert Word64 Int29_
instance NumConvert Word64 Int32
instance NumConvert Word64 Int64
instance NumExpand Word64 Int128
--instance NumExpand Word64 Int256
instance NumExpand Word64 Integer

instance NumConvert Word128 Word8
instance NumConvert Word128 Word16
instance NumConvert Word128 Word29_
instance NumConvert Word128 Word32
instance NumConvert Word128 Word64
instance NumExpand Word128 Word128
instance NumExpand Word128 Word256
instance NumConvert Word128 Int8
instance NumConvert Word128 Int16
instance NumConvert Word128 Int29_
instance NumConvert Word128 Int32
instance NumConvert Word128 Int64
instance NumConvert Word128 Int128
--instance NumExpand Word128 Int256
instance NumExpand Word128 Integer

instance NumConvert Word256 Word8
instance NumConvert Word256 Word16
instance NumConvert Word256 Word29_
instance NumConvert Word256 Word32
instance NumConvert Word256 Word64
instance NumConvert Word256 Word128
instance NumExpand Word256 Word256
instance NumConvert Word256 Int8
instance NumConvert Word256 Int16
instance NumConvert Word256 Int29_
instance NumConvert Word256 Int32
instance NumConvert Word256 Int64
instance NumConvert Word256 Int128
--instance NumConvert Word256 Int256
instance NumExpand Word256 Integer

instance NumConvert Int8 Word8
instance NumConvert Int8 Word16
instance NumConvert Int8 Word29_
instance NumConvert Int8 Word32
instance NumConvert Int8 Word64
instance NumConvert Int8 Word128
instance NumConvert Int8 Word256
instance NumExpand Int8 Int8
instance NumExpand Int8 Int16
instance NumExpand Int8 Int29_
instance NumExpand Int8 Int32
instance NumExpand Int8 Int64
instance NumExpand Int8 Int128
--instance NumExpand Int8 Int256
instance NumExpand Int8 Integer

instance NumConvert Int16 Word8
instance NumConvert Int16 Word16
instance NumConvert Int16 Word29_
instance NumConvert Int16 Word32
instance NumConvert Int16 Word64
instance NumConvert Int16 Word128
instance NumConvert Int16 Word256
instance NumConvert Int16 Int8
instance NumExpand Int16 Int16
instance NumExpand Int16 Int29_
instance NumExpand Int16 Int32
instance NumExpand Int16 Int64
instance NumExpand Int16 Int128
--instance NumExpand Int16 Int256
instance NumExpand Int16 Integer

instance NumConvert Int29_ Word8
instance NumConvert Int29_ Word16
instance NumConvert Int29_ Word29_
instance NumConvert Int29_ Word32
instance NumConvert Int29_ Word64
instance NumConvert Int29_ Word128
instance NumConvert Int29_ Word256
instance NumConvert Int29_ Int8
instance NumConvert Int29_ Int16
instance NumExpand Int29_ Int29_
instance NumConvert Int29_ Int32
instance NumConvert Int29_ Int64
instance NumConvert Int29_ Int128
--instance NumConvert Int29_ Int256
instance NumExpand Int29_ Integer

instance NumConvert Int32 Word8
instance NumConvert Int32 Word16
instance NumConvert Int32 Word29_
instance NumConvert Int32 Word32
instance NumConvert Int32 Word64
instance NumConvert Int32 Word128
instance NumConvert Int32 Word256
instance NumConvert Int32 Int8
instance NumConvert Int32 Int16
instance NumConvert Int32 Int29_
instance NumExpand Int32 Int32
instance NumExpand Int32 Int64
instance NumExpand Int32 Int128
--instance NumExpand Int32 Int256
instance NumExpand Int32 Integer

instance NumConvert Int64 Word8
instance NumConvert Int64 Word16
instance NumConvert Int64 Word29_
instance NumConvert Int64 Word32
instance NumConvert Int64 Word64
instance NumConvert Int64 Word128
instance NumConvert Int64 Word256
instance NumConvert Int64 Int8
instance NumConvert Int64 Int16
instance NumConvert Int64 Int29_
instance NumConvert Int64 Int32
instance NumExpand Int64 Int64
instance NumExpand Int64 Int128
--instance NumExpand Int64 Int256
instance NumExpand Int64 Integer

instance NumConvert Int128 Word8
instance NumConvert Int128 Word16
instance NumConvert Int128 Word29_
instance NumConvert Int128 Word32
instance NumConvert Int128 Word64
instance NumConvert Int128 Word128
instance NumConvert Int128 Word256
instance NumConvert Int128 Int8
instance NumConvert Int128 Int16
instance NumConvert Int128 Int29_
instance NumConvert Int128 Int32
instance NumConvert Int128 Int64
instance NumExpand Int128 Int128
--instance NumExpand Int128 Int256
instance NumExpand Int128 Integer

{-instance NumConvert Int256 Word8
instance NumConvert Int256 Word16
instance NumConvert Int256 Word29_
instance NumConvert Int256 Word32
instance NumConvert Int256 Word64
instance NumConvert Int256 Word128
instance NumConvert Int256 Word256
instance NumConvert Int256 Int8
instance NumConvert Int256 Int16
instance NumConvert Int256 Int29_
instance NumConvert Int256 Int32
instance NumConvert Int256 Int64
instance NumConvert Int256 Int128
instance NumExpand Int256 Int256
instance NumExpand Int256 Integer-}

instance NumConvert Integer Word8
instance NumConvert Integer Word16
instance NumConvert Integer Word29_
instance NumConvert Integer Word32
instance NumConvert Integer Word64
instance NumConvert Integer Word128
instance NumConvert Integer Word256
instance NumConvert Integer Int8
instance NumConvert Integer Int16
instance NumConvert Integer Int29_
instance NumConvert Integer Int32
instance NumConvert Integer Int64
instance NumConvert Integer Int128
--instance NumConvert Integer Int256
instance NumExpand Integer Integer

{- | Add with wrap-around.

Same as 'Prelude.+' but indicates to the reader that you explicitly thought
about this issue and decided that wrap-around is the correct behaviour.
-}
(+@) :: Num a => a -> a -> a
+@ :: a -> a -> a
(+@) = a -> a -> a
forall a. Num a => a -> a -> a
(+)
infixl 6 +@
{-# INLINE (+@) #-}

-- | Add with explicit error on overflow or underflow.
(+%) :: (Integral a, Bounded a) => a -> a -> Either ArithException a
+% :: a -> a -> Either ArithException a
(+%) a
a a
b = Integer -> Either ArithException a
forall a.
(Integral a, Bounded a) =>
Integer -> Either ArithException a
checkE (Integer -> Either ArithException a)
-> Integer -> Either ArithException a
forall a b. (a -> b) -> a -> b
$ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
b
infixl 6 +%
{-# INLINE (+%) #-}

-- | Add with output 'maxBound' on overflow or 'minBound' on underflow.
(+:) :: (Integral a, Bounded a) => a -> a -> a
+: :: a -> a -> a
(+:) a
a a
b = Integer -> a
forall a. (Integral a, Bounded a) => Integer -> a
checkS (Integer -> a) -> Integer -> a
forall a b. (a -> b) -> a -> b
$ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
b
infixl 6 +:
{-# INLINE (+:) #-}

-- | Add with runtime (async) exception on overflow or underflow.
(+!) :: (Integral a, Bounded a) => Partial => a -> a -> a
+! :: a -> a -> a
(+!) a
a a
b = Integer -> a
forall a. (Integral a, Bounded a, HasCallStack) => Integer -> a
checkX (Integer -> a) -> Integer -> a
forall a b. (a -> b) -> a -> b
$ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
b
infixl 6 +!
{-# INLINE (+!) #-}

{- | Subtract with wrap-around.

Same as 'Prelude.-' but indicates to the reader that you explicitly thought
about this issue and decided that wrap-around is the correct behaviour.
-}
(-@) :: Num a => a -> a -> a
-@ :: a -> a -> a
(-@) = (-)
infixl 6 -@
{-# INLINE (-@) #-}

-- | Subtract with explicit error on overflow or underflow.
(-%) :: (Integral a, Bounded a) => a -> a -> Either ArithException a
-% :: a -> a -> Either ArithException a
(-%) a
a a
b = Integer -> Either ArithException a
forall a.
(Integral a, Bounded a) =>
Integer -> Either ArithException a
checkE (Integer -> Either ArithException a)
-> Integer -> Either ArithException a
forall a b. (a -> b) -> a -> b
$ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- a -> Integer
forall a. Integral a => a -> Integer
toInteger a
b
infixl 6 -%
{-# INLINE (-%) #-}

-- | Subtract with output 'maxBound' on overflow or 'minBound' on underflow.
(-:) :: (Integral a, Bounded a) => a -> a -> a
-: :: a -> a -> a
(-:) a
a a
b = Integer -> a
forall a. (Integral a, Bounded a) => Integer -> a
checkS (Integer -> a) -> Integer -> a
forall a b. (a -> b) -> a -> b
$ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- a -> Integer
forall a. Integral a => a -> Integer
toInteger a
b
infixl 6 -:
{-# INLINE (-:) #-}

-- | Subtract with runtime (async) exception on overflow or underflow.
(-!) :: (Integral a, Bounded a) => Partial => a -> a -> a
-! :: a -> a -> a
(-!) a
a a
b = Integer -> a
forall a. (Integral a, Bounded a, HasCallStack) => Integer -> a
checkX (Integer -> a) -> Integer -> a
forall a b. (a -> b) -> a -> b
$ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- a -> Integer
forall a. Integral a => a -> Integer
toInteger a
b
infixl 6 -!
{-# INLINE (-!) #-}

{- | Multiply with wrap-around.

Same as 'Prelude.*' but indicates to the reader that you explicitly thought
about this issue and decided that wrap-around is the correct behaviour.
-}
(*@) :: Num a => a -> a -> a
*@ :: a -> a -> a
(*@) = a -> a -> a
forall a. Num a => a -> a -> a
(*)
infixl 7 *@
{-# INLINE (*@) #-}

-- | Multiply with explicit error on overflow or underflow.
(*%) :: (Integral a, Bounded a) => a -> a -> Either ArithException a
*% :: a -> a -> Either ArithException a
(*%) a
a a
b = Integer -> Either ArithException a
forall a.
(Integral a, Bounded a) =>
Integer -> Either ArithException a
checkE (Integer -> Either ArithException a)
-> Integer -> Either ArithException a
forall a b. (a -> b) -> a -> b
$ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* a -> Integer
forall a. Integral a => a -> Integer
toInteger a
b
infixl 7 *%
{-# INLINE (*%) #-}

-- | Multiply with output 'maxBound' on overflow or 'minBound' on underflow.
(*:) :: (Integral a, Bounded a) => a -> a -> a
*: :: a -> a -> a
(*:) a
a a
b = Integer -> a
forall a. (Integral a, Bounded a) => Integer -> a
checkS (Integer -> a) -> Integer -> a
forall a b. (a -> b) -> a -> b
$ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* a -> Integer
forall a. Integral a => a -> Integer
toInteger a
b
infixl 7 *:
{-# INLINE (*:) #-}

-- | Multiply with runtime (async) exception on overflow or underflow.
(*!) :: (Integral a, Bounded a) => Partial => a -> a -> a
*! :: a -> a -> a
(*!) a
a a
b = Integer -> a
forall a. (Integral a, Bounded a, HasCallStack) => Integer -> a
checkX (Integer -> a) -> Integer -> a
forall a b. (a -> b) -> a -> b
$ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* a -> Integer
forall a. Integral a => a -> Integer
toInteger a
b
infixl 7 *!
{-# INLINE (*!) #-}

{- | Power with wrap-around.

Same as 'Prelude.^' but indicates to the reader that you explicitly thought
about this issue and decided that wrap-around is the correct behaviour.
-}
(^@) :: Integral a => a -> a -> a
^@ :: a -> a -> a
(^@) = a -> a -> a
forall a b. (Num a, Integral b) => a -> b -> a
(^)
infixr 8 ^@
{-# INLINE (^@) #-}

-- | Power with explicit error on overflow or underflow.
(^%) :: (Integral a, Bounded a) => a -> a -> Either ArithException a
^% :: a -> a -> Either ArithException a
(^%) a
a a
b = Integer -> Either ArithException a
forall a.
(Integral a, Bounded a) =>
Integer -> Either ArithException a
checkE (Integer -> Either ArithException a)
-> Integer -> Either ArithException a
forall a b. (a -> b) -> a -> b
$ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
a Integer -> Integer -> Integer
forall a b. (Num a, Integral b) => a -> b -> a
^ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
b
infixr 8 ^%
{-# INLINE (^%) #-}

-- | Power with output 'maxBound' on overflow or 'minBound' on underflow.
(^:) :: (Integral a, Bounded a) => a -> a -> a
^: :: a -> a -> a
(^:) a
a a
b = Integer -> a
forall a. (Integral a, Bounded a) => Integer -> a
checkS (Integer -> a) -> Integer -> a
forall a b. (a -> b) -> a -> b
$ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
a Integer -> Integer -> Integer
forall a b. (Num a, Integral b) => a -> b -> a
^ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
b
infixr 8 ^:
{-# INLINE (^:) #-}

-- | Power with runtime (async) exception on overflow or underflow.
(^!) :: (Integral a, Bounded a) => Partial => a -> a -> a
^! :: a -> a -> a
(^!) a
a a
b = Integer -> a
forall a. (Integral a, Bounded a, HasCallStack) => Integer -> a
checkX (Integer -> a) -> Integer -> a
forall a b. (a -> b) -> a -> b
$ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
a Integer -> Integer -> Integer
forall a b. (Num a, Integral b) => a -> b -> a
^ a -> Integer
forall a. Integral a => a -> Integer
toInteger a
b
infixr 8 ^!
{-# INLINE (^!) #-}

{- | Type alias for a division-operation result with explicit error.

The @Left@ case means /division by zero/, and its parameter represents the
sign of the nominator operand.
-}
type DivResult a = Either Ordering a

explicitDiv :: (Eq a, Ord a, Num a) => (a -> a -> b) -> a -> a -> DivResult b
explicitDiv :: (a -> a -> b) -> a -> a -> DivResult b
explicitDiv a -> a -> b
op a
x a
y = if a
y a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
0 then Ordering -> DivResult b
forall a b. a -> Either a b
Left (Ordering -> DivResult b) -> Ordering -> DivResult b
forall a b. (a -> b) -> a -> b
$ a -> a -> Ordering
forall a. Ord a => a -> a -> Ordering
compare a
x a
0 else b -> DivResult b
forall a b. b -> Either a b
Right (b -> DivResult b) -> b -> DivResult b
forall a b. (a -> b) -> a -> b
$ a
x a -> a -> b
`op` a
y
{-# INLINE explicitDiv #-}

-- | Division (truncated towards -Inf) with explicit error on division-by-zero.
divE :: Integral a => a -> a -> DivResult a
divE :: a -> a -> DivResult a
divE = (a -> a -> a) -> a -> a -> DivResult a
forall a b.
(Eq a, Ord a, Num a) =>
(a -> a -> b) -> a -> a -> DivResult b
explicitDiv a -> a -> a
forall a. Integral a => a -> a -> a
div
{-# INLINE divE #-}

{- | Division (truncated towards -Inf) with runtime (async) exception on division-by-zero.

Same as 'Prelude.div' but indicates to the reader that you explicitly thought
about this issue and decided that runtime exception is the correct behaviour.
-}
divX :: Integral a => a -> a -> a
divX :: a -> a -> a
divX = a -> a -> a
forall a. Integral a => a -> a -> a
div
{-# INLINE divX #-}

-- | Modulus (truncated towards -Inf) with explicit error on division-by-zero.
modE :: Integral a => a -> a -> DivResult a
modE :: a -> a -> DivResult a
modE = (a -> a -> a) -> a -> a -> DivResult a
forall a b.
(Eq a, Ord a, Num a) =>
(a -> a -> b) -> a -> a -> DivResult b
explicitDiv a -> a -> a
forall a. Integral a => a -> a -> a
mod
{-# INLINE modE #-}

{- | Modulus (truncated towards -Inf) with runtime (async) exception on division-by-zero.

Same as 'Prelude.mod' but indicates to the reader that you explicitly thought
about this issue and decided that runtime exception is the correct behaviour.
-}
modX :: Integral a => a -> a -> a
modX :: a -> a -> a
modX = a -> a -> a
forall a. Integral a => a -> a -> a
mod
{-# INLINE modX #-}

-- | Division-and-modulus (truncated towards -Inf) with explicit error on division-by-zero.
divModE :: Integral a => a -> a -> DivResult (a, a)
divModE :: a -> a -> DivResult (a, a)
divModE = (a -> a -> (a, a)) -> a -> a -> DivResult (a, a)
forall a b.
(Eq a, Ord a, Num a) =>
(a -> a -> b) -> a -> a -> DivResult b
explicitDiv a -> a -> (a, a)
forall a. Integral a => a -> a -> (a, a)
divMod
{-# INLINE divModE #-}

{- | Division-and-modulus (truncated towards -Inf) with runtime (async) exception on division-by-zero.

Same as 'Prelude.divMod' but indicates to the reader that you explicitly thought
about this issue and decided that runtime exception is the correct behaviour.
-}
divModX :: Integral a => a -> a -> (a, a)
divModX :: a -> a -> (a, a)
divModX = a -> a -> (a, a)
forall a. Integral a => a -> a -> (a, a)
divMod
{-# INLINE divModX #-}

-- | Division (truncated towards 0) with explicit error on division-by-zero.
quotE :: Integral a => a -> a -> DivResult a
quotE :: a -> a -> DivResult a
quotE = (a -> a -> a) -> a -> a -> DivResult a
forall a b.
(Eq a, Ord a, Num a) =>
(a -> a -> b) -> a -> a -> DivResult b
explicitDiv a -> a -> a
forall a. Integral a => a -> a -> a
quot
{-# INLINE quotE #-}

{- | Division (truncated towards 0) with runtime (async) exception on division-by-zero.

Same as 'Prelude.quot' but indicates to the reader that you explicitly thought
about this issue and decided that runtime exception is the correct behaviour.
-}
quotX :: Integral a => a -> a -> a
quotX :: a -> a -> a
quotX = a -> a -> a
forall a. Integral a => a -> a -> a
quot
{-# INLINE quotX #-}

-- | Modulus (truncated towards 0) with explicit error on division-by-zero.
remE :: Integral a => a -> a -> DivResult a
remE :: a -> a -> DivResult a
remE = (a -> a -> a) -> a -> a -> DivResult a
forall a b.
(Eq a, Ord a, Num a) =>
(a -> a -> b) -> a -> a -> DivResult b
explicitDiv a -> a -> a
forall a. Integral a => a -> a -> a
rem
{-# INLINE remE #-}

{- | Modulus (truncated towards 0) with runtime (async) exception on division-by-zero.

Same as 'Prelude.rem' but indicates to the reader that you explicitly thought
about this issue and decided that runtime exception is the correct behaviour.
-}
remX :: Integral a => a -> a -> a
remX :: a -> a -> a
remX = a -> a -> a
forall a. Integral a => a -> a -> a
rem
{-# INLINE remX #-}

-- | Division-and-modulus (truncated towards 0) with explicit error on division-by-zero.
quotRemE :: Integral a => a -> a -> DivResult (a, a)
quotRemE :: a -> a -> DivResult (a, a)
quotRemE = (a -> a -> (a, a)) -> a -> a -> DivResult (a, a)
forall a b.
(Eq a, Ord a, Num a) =>
(a -> a -> b) -> a -> a -> DivResult b
explicitDiv a -> a -> (a, a)
forall a. Integral a => a -> a -> (a, a)
quotRem
{-# INLINE quotRemE #-}

{- | Division-and-modulus (truncated towards 0) with runtime (async) exception on division-by-zero.

Same as 'Prelude.quotRem' but indicates to the reader that you explicitly thought
about this issue and decided that runtime exception is the correct behaviour.
-}
quotRemX :: Integral a => a -> a -> (a, a)
quotRemX :: a -> a -> (a, a)
quotRemX = a -> a -> (a, a)
forall a. Integral a => a -> a -> (a, a)
quotRem
{-# INLINE quotRemX #-}