-- | Provides the 'AGroup' typeclass.
--
-- @since 0.1
module Numeric.Algebra.Additive.AGroup
  ( AGroup (..),
    anegate,
  )
where

import Data.Coerce (coerce)
import Data.Complex (Complex)
import Data.Fixed (Fixed, HasResolution)
import Data.Int (Int16, Int32, Int64, Int8)
import Data.Kind (Constraint, Type)
import Data.Ratio (Ratio)
import Data.Word (Word16, Word32, Word64, Word8)
import Numeric.Algebra.Additive.AMonoid (AMonoid (zero))
import Numeric.Algebra.Deriving
  ( AsFractional (MkAsFractional),
    AsIntegral (MkAsIntegral),
    AsNum (MkAsNum),
  )

-- $setup
-- >>> import Numeric.Algebra.Additive.ASemigroup ((.+.))

-- | Defines an additive group.
--
-- ==== __Examples:__
--
-- >>> :{
--   -- Addition
--   f1 :: (AGroup g) => g -> g
--   f1 x = x .+. x
-- :}
--
-- >>> f1 5
-- 10
--
-- >>> :{
--   -- Zero
--   f2 :: (AGroup g) => g -> g
--   f2 x = x .+. zero
-- :}
--
-- >>> f2 5
-- 5
--
-- >>> :{
--   -- Subtraction
--   f3 :: (AGroup g, Num g) => g -> g
--   f3 x = x .-. 3
-- :}
--
-- >>> f3 5
-- 2
--
-- @since 0.1
type AGroup :: Type -> Constraint
class (AMonoid g) => AGroup g where
  -- | Should satisfy:
  --
  -- @
  -- -- inverse
  -- a .-. a === zero
  -- @
  --
  -- @since 0.1
  (.-.) :: g -> g -> g

infixl 6 .-.

-- | @since 0.1
anegate :: (AGroup g) => g -> g
anegate :: forall g. AGroup g => g -> g
anegate g
n = g
forall m. AMonoid m => m
zero g -> g -> g
forall g. AGroup g => g -> g -> g
.-. g
n
{-# INLINE anegate #-}

-- | @since 0.1
deriving via (AsNum a) instance (Num a) => AGroup (AsFractional a)

-- | @since 0.1
deriving via (AsNum a) instance (Num a) => AGroup (AsIntegral a)

-- | @since 0.1
instance (Num a) => AGroup (AsNum a) where
  .-. :: AsNum a -> AsNum a -> AsNum a
(.-.) = forall a b. Coercible a b => a -> b
forall a b. Coercible a b => a -> b
coerce @(a -> a -> a) @(AsNum a -> AsNum a -> AsNum a) (-)
  {-# INLINE (.-.) #-}

-- | @since 0.1
deriving via (AsNum Double) instance AGroup Double

-- | @since 0.1
deriving via (AsNum Float) instance AGroup Float

-- | @since 0.1
deriving via (AsNum Int) instance AGroup Int

-- | @since 0.1
deriving via (AsNum Int8) instance AGroup Int8

-- | @since 0.1
deriving via (AsNum Int16) instance AGroup Int16

-- | @since 0.1
deriving via (AsNum Int32) instance AGroup Int32

-- | @since 0.1
deriving via (AsNum Int64) instance AGroup Int64

-- | @since 0.1
deriving via (AsNum Integer) instance AGroup Integer

-- | @since 0.1
deriving via (AsNum Word) instance AGroup Word

-- | @since 0.1
deriving via (AsNum Word8) instance AGroup Word8

-- | @since 0.1
deriving via (AsNum Word16) instance AGroup Word16

-- | @since 0.1
deriving via (AsNum Word32) instance AGroup Word32

-- | @since 0.1
deriving via (AsNum Word64) instance AGroup Word64

-- | @since 0.1
deriving via (AsNum (Ratio Integer)) instance AGroup (Ratio Integer)

-- | @since 0.1
deriving via (AsNum (Complex a)) instance (RealFloat a) => AGroup (Complex a)

-- | @since 0.1
deriving via (AsNum (Fixed k)) instance (HasResolution k) => AGroup (Fixed k)

-- | @since 0.1
instance (AGroup a) => AGroup (a, a) where
  (a
x1, a
x2) .-. :: (a, a) -> (a, a) -> (a, a)
.-. (a
y1, a
y2) = (a
x1 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y1, a
x2 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y2)
  {-# INLINE (.-.) #-}

-- | @since 0.1
instance (AGroup a) => AGroup (a, a, a) where
  (a
x1, a
x2, a
x3) .-. :: (a, a, a) -> (a, a, a) -> (a, a, a)
.-. (a
y1, a
y2, a
y3) = (a
x1 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y1, a
x2 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y2, a
x3 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y3)
  {-# INLINE (.-.) #-}

-- | @since 0.1
instance (AGroup a) => AGroup (a, a, a, a) where
  (a
x1, a
x2, a
x3, a
x4) .-. :: (a, a, a, a) -> (a, a, a, a) -> (a, a, a, a)
.-. (a
y1, a
y2, a
y3, a
y4) =
    ( a
x1 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y1,
      a
x2 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y2,
      a
x3 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y3,
      a
x4 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y4
    )
  {-# INLINE (.-.) #-}

-- | @since 0.1
instance (AGroup a) => AGroup (a, a, a, a, a) where
  (a
x1, a
x2, a
x3, a
x4, a
x5) .-. :: (a, a, a, a, a) -> (a, a, a, a, a) -> (a, a, a, a, a)
.-. (a
y1, a
y2, a
y3, a
y4, a
y5) =
    ( a
x1 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y1,
      a
x2 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y2,
      a
x3 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y3,
      a
x4 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y4,
      a
x5 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y5
    )
  {-# INLINE (.-.) #-}

-- | @since 0.1
instance (AGroup a) => AGroup (a, a, a, a, a, a) where
  (a
x1, a
x2, a
x3, a
x4, a
x5, a
x6) .-. :: (a, a, a, a, a, a) -> (a, a, a, a, a, a) -> (a, a, a, a, a, a)
.-. (a
y1, a
y2, a
y3, a
y4, a
y5, a
y6) =
    ( a
x1 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y1,
      a
x2 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y2,
      a
x3 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y3,
      a
x4 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y4,
      a
x5 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y5,
      a
x6 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y6
    )
  {-# INLINE (.-.) #-}

-- | @since 0.1
instance (AGroup a) => AGroup (a, a, a, a, a, a, a) where
  (a
x1, a
x2, a
x3, a
x4, a
x5, a
x6, a
x7) .-. :: (a, a, a, a, a, a, a)
-> (a, a, a, a, a, a, a) -> (a, a, a, a, a, a, a)
.-. (a
y1, a
y2, a
y3, a
y4, a
y5, a
y6, a
y7) =
    ( a
x1 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y1,
      a
x2 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y2,
      a
x3 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y3,
      a
x4 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y4,
      a
x5 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y5,
      a
x6 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y6,
      a
x7 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y7
    )
  {-# INLINE (.-.) #-}

-- | @since 0.1
instance (AGroup a) => AGroup (a, a, a, a, a, a, a, a) where
  (a
x1, a
x2, a
x3, a
x4, a
x5, a
x6, a
x7, a
x8) .-. :: (a, a, a, a, a, a, a, a)
-> (a, a, a, a, a, a, a, a) -> (a, a, a, a, a, a, a, a)
.-. (a
y1, a
y2, a
y3, a
y4, a
y5, a
y6, a
y7, a
y8) =
    ( a
x1 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y1,
      a
x2 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y2,
      a
x3 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y3,
      a
x4 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y4,
      a
x5 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y5,
      a
x6 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y6,
      a
x7 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y7,
      a
x8 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y8
    )
  {-# INLINE (.-.) #-}

-- | @since 0.1
instance (AGroup a) => AGroup (a, a, a, a, a, a, a, a, a) where
  (a
x1, a
x2, a
x3, a
x4, a
x5, a
x6, a
x7, a
x8, a
x9) .-. :: (a, a, a, a, a, a, a, a, a)
-> (a, a, a, a, a, a, a, a, a) -> (a, a, a, a, a, a, a, a, a)
.-. (a
y1, a
y2, a
y3, a
y4, a
y5, a
y6, a
y7, a
y8, a
y9) =
    ( a
x1 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y1,
      a
x2 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y2,
      a
x3 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y3,
      a
x4 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y4,
      a
x5 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y5,
      a
x6 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y6,
      a
x7 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y7,
      a
x8 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y8,
      a
x9 a -> a -> a
forall g. AGroup g => g -> g -> g
.-. a
y9
    )
  {-# INLINE (.-.) #-}