{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}

-- |
-- Module: Numeric.Additive
-- Copyright: Copyright © 2018 Kadena LLC.
-- License: MIT
-- Maintainer: Lars Kuhtz <lars@kadena.io>
-- Stability: experimental
--
-- Haskell's @Num@ class doesn't support fine grained control
-- over what arithmetic operations are defined for a type.
-- Sometimes only some operations have a well defined semantics
-- and @Num@ instances are notorious for including undefined/error
-- values or unlawful workarounds.
--
module Numeric.Additive
(
-- * Additive Semigroup
  AdditiveSemigroup(..)
, AdditiveAbelianSemigroup
, (^+^)

-- * Additive Monoid
, AdditiveMonoid(..)
, AdditiveAbelianMonoid

-- * Additive Group
, AdditiveGroup(..)

-- * Additive Abelian Group
, AdditiveAbelianGroup
, (^-^)
) where

import Data.DoubleWord
import Data.Int
import Data.Word

import Numeric.Natural

-- -------------------------------------------------------------------------- --
-- | Additive Semigroup
--
-- prop> (a `plus` b) `plus` c == a `plus` (b `plus` c)
--
class AdditiveSemigroup g where
    plus :: g -> g -> g

instance AdditiveSemigroup Integer where
    plus :: Integer -> Integer -> Integer
plus = Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
(+)
    {-# INLINE plus #-}

instance AdditiveSemigroup Rational where
    plus :: Rational -> Rational -> Rational
plus = Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
(+)
    {-# INLINE plus #-}

instance AdditiveSemigroup Natural where
    plus :: Natural -> Natural -> Natural
plus = Natural -> Natural -> Natural
forall a. Num a => a -> a -> a
(+)
    {-# INLINE plus #-}

instance AdditiveSemigroup Int where
    plus :: Int -> Int -> Int
plus = Int -> Int -> Int
forall a. Num a => a -> a -> a
(+)
    {-# INLINE plus #-}

instance AdditiveSemigroup Word where
    plus :: Word -> Word -> Word
plus = Word -> Word -> Word
forall a. Num a => a -> a -> a
(+)
    {-# INLINE plus #-}

instance AdditiveSemigroup Word8 where
    plus :: Word8 -> Word8 -> Word8
plus = Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
(+)
    {-# INLINE plus #-}

instance AdditiveSemigroup Word16 where
    plus :: Word16 -> Word16 -> Word16
plus = Word16 -> Word16 -> Word16
forall a. Num a => a -> a -> a
(+)
    {-# INLINE plus #-}

instance AdditiveSemigroup Word32 where
    plus :: Word32 -> Word32 -> Word32
plus = Word32 -> Word32 -> Word32
forall a. Num a => a -> a -> a
(+)
    {-# INLINE plus #-}

instance AdditiveSemigroup Word64 where
    plus :: Word64 -> Word64 -> Word64
plus = Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
(+)
    {-# INLINE plus #-}

instance AdditiveSemigroup Word128 where
    plus :: Word128 -> Word128 -> Word128
plus = Word128 -> Word128 -> Word128
forall a. Num a => a -> a -> a
(+)
    {-# INLINE plus #-}

instance AdditiveSemigroup Word256 where
    plus :: Word256 -> Word256 -> Word256
plus = Word256 -> Word256 -> Word256
forall a. Num a => a -> a -> a
(+)
    {-# INLINE plus #-}

instance AdditiveSemigroup Int8 where
    plus :: Int8 -> Int8 -> Int8
plus = Int8 -> Int8 -> Int8
forall a. Num a => a -> a -> a
(+)
    {-# INLINE plus #-}

instance AdditiveSemigroup Int16 where
    plus :: Int16 -> Int16 -> Int16
plus = Int16 -> Int16 -> Int16
forall a. Num a => a -> a -> a
(+)
    {-# INLINE plus #-}

instance AdditiveSemigroup Int32 where
    plus :: Int32 -> Int32 -> Int32
plus = Int32 -> Int32 -> Int32
forall a. Num a => a -> a -> a
(+)
    {-# INLINE plus #-}

instance AdditiveSemigroup Int64 where
    plus :: Int64 -> Int64 -> Int64
plus = Int64 -> Int64 -> Int64
forall a. Num a => a -> a -> a
(+)
    {-# INLINE plus #-}

-- -------------------------------------------------------------------------- --
-- | Additive Abelian Semigroup
--
-- prop> a `plus` b == b `plus` a
--
class AdditiveSemigroup g => AdditiveAbelianSemigroup g

instance AdditiveAbelianSemigroup Integer
instance AdditiveAbelianSemigroup Rational
instance AdditiveAbelianSemigroup Natural
instance AdditiveAbelianSemigroup Int
instance AdditiveAbelianSemigroup Int8
instance AdditiveAbelianSemigroup Int16
instance AdditiveAbelianSemigroup Int32
instance AdditiveAbelianSemigroup Int64
instance AdditiveAbelianSemigroup Word
instance AdditiveAbelianSemigroup Word8
instance AdditiveAbelianSemigroup Word16
instance AdditiveAbelianSemigroup Word32
instance AdditiveAbelianSemigroup Word64
instance AdditiveAbelianSemigroup Word128
instance AdditiveAbelianSemigroup Word256

infixl 6 ^+^
(^+^) :: AdditiveAbelianSemigroup g => g -> g -> g
^+^ :: forall g. AdditiveAbelianSemigroup g => g -> g -> g
(^+^) = g -> g -> g
forall g. AdditiveSemigroup g => g -> g -> g
plus
{-# INLINE (^+^) #-}

-- -------------------------------------------------------------------------- --
-- | Additive Monoid
--
-- prop> a `plus` zero == a
-- prop> zero `plus` a == a
--
class AdditiveSemigroup g => AdditiveMonoid g where
    zero :: g

instance AdditiveMonoid Integer where
    zero :: Integer
zero = Integer
0
    {-# INLINE zero #-}

instance AdditiveMonoid Rational where
    zero :: Rational
zero = Rational
0
    {-# INLINE zero #-}

instance AdditiveMonoid Natural where
    zero :: Natural
zero = Natural
0
    {-# INLINE zero #-}

instance AdditiveMonoid Int where
    zero :: Int
zero = Int
0
    {-# INLINE zero #-}

instance AdditiveMonoid Word where
    zero :: Word
zero = Word
0
    {-# INLINE zero #-}

instance AdditiveMonoid Word8 where
    zero :: Word8
zero = Word8
0
    {-# INLINE zero #-}

instance AdditiveMonoid Word16 where
    zero :: Word16
zero = Word16
0
    {-# INLINE zero #-}

instance AdditiveMonoid Word32 where
    zero :: Word32
zero = Word32
0
    {-# INLINE zero #-}

instance AdditiveMonoid Word64 where
    zero :: Word64
zero = Word64
0
    {-# INLINE zero #-}

instance AdditiveMonoid Word128 where
    zero :: Word128
zero = Word128
0
    {-# INLINE zero #-}

instance AdditiveMonoid Word256 where
    zero :: Word256
zero = Word256
0
    {-# INLINE zero #-}

instance AdditiveMonoid Int8 where
    zero :: Int8
zero = Int8
0
    {-# INLINE zero #-}

instance AdditiveMonoid Int16 where
    zero :: Int16
zero = Int16
0
    {-# INLINE zero #-}

instance AdditiveMonoid Int32 where
    zero :: Int32
zero = Int32
0
    {-# INLINE zero #-}

instance AdditiveMonoid Int64 where
    zero :: Int64
zero = Int64
0
    {-# INLINE zero #-}

type AdditiveAbelianMonoid g = (AdditiveMonoid g, AdditiveAbelianSemigroup g)

-- -------------------------------------------------------------------------- --
-- | Additive Group
--
-- prop> a `plus` inverse a == zero
-- prop> inverse a `plus` a == zero
--
class AdditiveMonoid g => AdditiveGroup g where
    invert :: g -> g
    invert g
a = g
forall g. AdditiveMonoid g => g
zero g -> g -> g
forall g. AdditiveGroup g => g -> g -> g
`minus` g
a

    minus :: g -> g -> g
    minus g
a g
b = g
a g -> g -> g
forall g. AdditiveSemigroup g => g -> g -> g
`plus` g -> g
forall g. AdditiveGroup g => g -> g
invert g
b

    {-# MINIMAL invert | minus #-}

instance AdditiveGroup Integer where
    invert :: Integer -> Integer
invert Integer
a = -Integer
a
    minus :: Integer -> Integer -> Integer
minus = (-)
    {-# INLINE invert #-}
    {-# INLINE minus #-}

instance AdditiveGroup Rational where
    invert :: Rational -> Rational
invert Rational
a = -Rational
a
    minus :: Rational -> Rational -> Rational
minus = (-)
    {-# INLINE invert #-}
    {-# INLINE minus #-}

instance AdditiveGroup Int where
    invert :: Int -> Int
invert Int
a = -Int
a
    minus :: Int -> Int -> Int
minus = (-)
    {-# INLINE invert #-}
    {-# INLINE minus #-}

instance AdditiveGroup Word where
    invert :: Word -> Word
invert Word
a = -Word
a
    minus :: Word -> Word -> Word
minus = (-)
    {-# INLINE invert #-}
    {-# INLINE minus #-}

instance AdditiveGroup Word8 where
    invert :: Word8 -> Word8
invert Word8
a = -Word8
a
    minus :: Word8 -> Word8 -> Word8
minus = (-)
    {-# INLINE invert #-}
    {-# INLINE minus #-}

instance AdditiveGroup Word16 where
    invert :: Word16 -> Word16
invert Word16
a = -Word16
a
    minus :: Word16 -> Word16 -> Word16
minus = (-)
    {-# INLINE invert #-}
    {-# INLINE minus #-}

instance AdditiveGroup Word32 where
    invert :: Word32 -> Word32
invert Word32
a = -Word32
a
    minus :: Word32 -> Word32 -> Word32
minus = (-)
    {-# INLINE invert #-}
    {-# INLINE minus #-}

instance AdditiveGroup Word64 where
    invert :: Word64 -> Word64
invert Word64
a = -Word64
a
    minus :: Word64 -> Word64 -> Word64
minus = (-)
    {-# INLINE invert #-}
    {-# INLINE minus #-}

instance AdditiveGroup Word128 where
    invert :: Word128 -> Word128
invert Word128
a = -Word128
a
    minus :: Word128 -> Word128 -> Word128
minus = (-)
    {-# INLINE invert #-}
    {-# INLINE minus #-}

instance AdditiveGroup Word256 where
    invert :: Word256 -> Word256
invert Word256
a = -Word256
a
    minus :: Word256 -> Word256 -> Word256
minus = (-)
    {-# INLINE invert #-}
    {-# INLINE minus #-}

instance AdditiveGroup Int8 where
    invert :: Int8 -> Int8
invert Int8
a = -Int8
a
    minus :: Int8 -> Int8 -> Int8
minus = (-)
    {-# INLINE invert #-}
    {-# INLINE minus #-}

instance AdditiveGroup Int16 where
    invert :: Int16 -> Int16
invert Int16
a = -Int16
a
    minus :: Int16 -> Int16 -> Int16
minus = (-)
    {-# INLINE invert #-}
    {-# INLINE minus #-}

instance AdditiveGroup Int32 where
    invert :: Int32 -> Int32
invert Int32
a = -Int32
a
    minus :: Int32 -> Int32 -> Int32
minus = (-)
    {-# INLINE invert #-}
    {-# INLINE minus #-}

instance AdditiveGroup Int64 where
    invert :: Int64 -> Int64
invert Int64
a = -Int64
a
    minus :: Int64 -> Int64 -> Int64
minus = (-)
    {-# INLINE invert #-}
    {-# INLINE minus #-}

-- -------------------------------------------------------------------------- --
-- | Additive Abelian Group
--
type AdditiveAbelianGroup g = (AdditiveGroup g, AdditiveAbelianMonoid g)

infix 6 ^-^
(^-^) :: AdditiveAbelianGroup g => g -> g -> g
^-^ :: forall g. AdditiveAbelianGroup g => g -> g -> g
(^-^) = g -> g -> g
forall g. AdditiveGroup g => g -> g -> g
minus
{-# INLINE (^-^) #-}