module Data.Semigroup.Act where

import Data.Semigroup
import qualified Numeric.Natural.Internal as NI

-- | Represents an action of semigroup @g@ to set @a@.
--
-- Laws: @'Endo' . 'act'@ must be a homomorphism of semigroups.
class Semigroup g => SemigroupAct g a where
    act :: g -> (a -> a)

-- | Represents an action of monoid @g@ to set @a@.
--
-- Laws: @'Endo' . 'act'@ must be a homomorphism of monoids.
class (Monoid g, SemigroupAct g a) => MonoidAct g a where

-- | A wrapper for constructing a monoid action from 'Option'.
newtype OptionSet g a = OptionSet { getOptionSet :: a }

instance (SemigroupAct g a, Semigroup g)
  => SemigroupAct (Option g) (OptionSet g a) where
    act (Option Nothing)  x = x
    act (Option (Just g)) (OptionSet x) = OptionSet $ (act g) x
instance (SemigroupAct g a, Monoid g)
  => MonoidAct (Option g) (OptionSet g a) where


-- | A wrapper for a group acting on itself.
newtype SelfAct a = SelfAct a
  deriving (Show, Read, Eq, Ord)
instance Functor SelfAct where
    fmap f (SelfAct x) = SelfAct (f x)
instance Semigroup g => Semigroup (SelfAct g) where
    (SelfAct x) <> (SelfAct y) = SelfAct $ x <> y
instance (Monoid g) => Monoid (SelfAct g) where
    mempty = SelfAct mempty
    mappend (SelfAct x) (SelfAct y) = SelfAct $ x `mappend` y
instance Semigroup g => SemigroupAct (SelfAct g) (SelfAct g) where
    act = (<>)
instance (Semigroup g, Monoid g) => MonoidAct (SelfAct g) (SelfAct g) where

-- | A wrapper for represeting the action of natural numbers with
-- multiplication on a monoid.
newtype Repeat a = Repeat { unwrapRepeat :: a }
  deriving (Show, Read, Eq, Ord)
instance Functor Repeat where
    fmap f (Repeat x) = Repeat (f x)
-- | The implementation uses 'times1p' which is defined very efficiently
-- for most semigroups.
instance (Monoid w, NI.Whole n) => SemigroupAct (Product n) (Repeat w) where
    act (Product m) (Repeat x)
        | m == 0    = Repeat mempty
        | otherwise = Repeat . unwrapMonoid $ times1p (NI.unsafePred m) (WrapMonoid x)