{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveFunctor, GeneralizedNewtypeDeriving #-}
module NumHask.Data where

import Data.Coerce (coerce)
import GHC.Generics
import NumHask.Algebra
import Prelude hiding (Num(..), sum, recip)

#if !MIN_VERSION_base(4,11,0)
import Data.Semigroup ((<>), Semigroup)
#endif

-- | Monoid under addition.
--
-- >>> getSum (Sum 1 <> Sum 2 <> mempty)
-- 3
newtype Sum a = Sum { getSum :: a }
        deriving (Eq, Ord, Read, Show, Bounded, Generic, Generic1, Functor)

-- | @since 4.8.0.0
instance Applicative Sum where
    pure     = Sum
    (<*>)    = coerce  

-- | @since 4.8.0.0
instance Monad Sum where
    m >>= k  = k (getSum m)

instance AdditiveMagma a => AdditiveMagma (Sum a) where
  (Sum x) `plus` (Sum y) = Sum (x `plus` y)

instance AdditiveUnital a => AdditiveUnital (Sum a) where
  zero = Sum zero

instance AdditiveMagma a => AdditiveAssociative (Sum a)

instance AdditiveInvertible a => AdditiveInvertible (Sum a) where
  negate (Sum x) = Sum (negate x)

instance AdditiveMagma a => AdditiveCommutative (Sum a) where

instance (AdditiveUnital a, AdditiveMagma a) => Additive (Sum a) where  

instance (AdditiveInvertible a, AdditiveUnital a) => AdditiveGroup (Sum a) where


instance AdditiveMagma a => Semigroup (Sum a) where
  (Sum x) <> (Sum y) = Sum $ x `plus` y

instance AdditiveUnital a => Monoid (Sum a) where
  mempty = Sum zero




-- | Monoid under multiplication.
--
-- >>> getProduct (Product 3 <> Product 4 <> mempty)
-- 12
newtype Product a = Product { getProduct :: a }
        deriving (Eq, Ord, Read, Show, Bounded, Generic, Generic1, Functor)

-- | @since 4.8.0.0
instance Applicative Product where
    pure     = Product
    (<*>)    = coerce

-- | @since 4.8.0.0
instance Monad Product where
    m >>= k  = k (getProduct m)


instance MultiplicativeMagma a => MultiplicativeMagma (Product a) where
  (Product x) `times` (Product y) = Product (x `times` y)

instance MultiplicativeUnital a => MultiplicativeUnital (Product a) where
  one = Product one

instance MultiplicativeMagma a => MultiplicativeAssociative (Product a) 

instance MultiplicativeInvertible a => MultiplicativeInvertible (Product a) where
  recip (Product x) = Product (recip x)

instance MultiplicativeMagma a => MultiplicativeCommutative (Product a)

instance MultiplicativeUnital a => Multiplicative (Product a) where

instance (MultiplicativeUnital a, MultiplicativeInvertible a) => MultiplicativeGroup (Product a) where


instance MultiplicativeMagma a => Semigroup (Product a) where
  (Product x) <> (Product y) = Product $ x `times` y

instance MultiplicativeUnital a => Monoid (Product a) where
  mempty = Product one