-- | Provides the 'ASemigroup' typeclass.
--
-- @since 0.1
module Numeric.Algebra.Additive.ASemigroup
  ( ASemigroup (..),
  )
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 GHC.Natural (Natural)
import Numeric.Algebra.Deriving
  ( AsFractional (MkAsFractional),
    AsIntegral (MkAsIntegral),
    AsNum (MkAsNum),
  )

-- | Defines an additive semigroup.
--
-- ==== __Examples:__
--
-- >>> :{
--   -- Addition
--   f1 :: (ASemigroup g) => g -> g
--   f1 x = x .+. x
-- :}
--
-- >>> f1 5
-- 10
--
-- @since 0.1
type ASemigroup :: Type -> Constraint
class ASemigroup s where
  -- | Should satisfy:
  --
  -- @
  -- -- associativity
  -- a .+. (b .+. c) === (a .+. b) .+. c
  -- @
  --
  -- @since 0.1
  (.+.) :: s -> s -> s

infixl 6 .+.

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

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

-- | @since 0.1
instance (Num a) => ASemigroup (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) a -> a -> a
forall a. Num a => a -> a -> a
(+)
  {-# INLINE (.+.) #-}

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

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

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

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

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

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

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

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

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

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

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

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

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

-- | @since 0.1
deriving via (AsNum Natural) instance ASemigroup Natural

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

-- | @since 0.1
deriving via (AsNum (Ratio Natural)) instance ASemigroup (Ratio Natural)

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

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

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

-- | @since 0.1
instance (ASemigroup a) => ASemigroup (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 s. ASemigroup s => s -> s -> s
.+. a
y1, a
x2 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y2, a
x3 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y3)
  {-# INLINE (.+.) #-}

-- | @since 0.1
instance (ASemigroup a) => ASemigroup (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 s. ASemigroup s => s -> s -> s
.+. a
y1,
      a
x2 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y2,
      a
x3 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y3,
      a
x4 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y4
    )
  {-# INLINE (.+.) #-}

-- | @since 0.1
instance (ASemigroup a) => ASemigroup (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 s. ASemigroup s => s -> s -> s
.+. a
y1,
      a
x2 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y2,
      a
x3 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y3,
      a
x4 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y4,
      a
x5 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y5
    )
  {-# INLINE (.+.) #-}

-- | @since 0.1
instance (ASemigroup a) => ASemigroup (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 s. ASemigroup s => s -> s -> s
.+. a
y1,
      a
x2 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y2,
      a
x3 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y3,
      a
x4 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y4,
      a
x5 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y5,
      a
x6 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y6
    )
  {-# INLINE (.+.) #-}

-- | @since 0.1
instance (ASemigroup a) => ASemigroup (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 s. ASemigroup s => s -> s -> s
.+. a
y1,
      a
x2 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y2,
      a
x3 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y3,
      a
x4 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y4,
      a
x5 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y5,
      a
x6 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y6,
      a
x7 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y7
    )
  {-# INLINE (.+.) #-}

-- | @since 0.1
instance (ASemigroup a) => ASemigroup (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 s. ASemigroup s => s -> s -> s
.+. a
y1,
      a
x2 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y2,
      a
x3 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y3,
      a
x4 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y4,
      a
x5 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y5,
      a
x6 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y6,
      a
x7 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y7,
      a
x8 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y8
    )
  {-# INLINE (.+.) #-}

-- | @since 0.1
instance (ASemigroup a) => ASemigroup (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 s. ASemigroup s => s -> s -> s
.+. a
y1,
      a
x2 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y2,
      a
x3 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y3,
      a
x4 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y4,
      a
x5 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y5,
      a
x6 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y6,
      a
x7 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y7,
      a
x8 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y8,
      a
x9 a -> a -> a
forall s. ASemigroup s => s -> s -> s
.+. a
y9
    )
  {-# INLINE (.+.) #-}