{-# LANGUAGE
    CPP
  , FlexibleContexts
  , MonoLocalBinds
  , ScopedTypeVariables
  , TypeApplications
  , UndecidableInstances
#-}

{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# OPTIONS_GHC -fno-warn-unused-imports #-}

{-|
Module: Data.Group.Generics

Orphan instances allowing generic deriving of 'Group' instances:

>  > data MyRecord
>  >   = MyRecord
>  >   { field1 :: Sum Double
>  >   , field2 :: Product Double
>  >   , field3 :: ( Sum Int, Sum Int )
>  >   }
>  >   deriving Generic
>  >   deriving ( Semigroup, Monoid, Group )
>  >     via Generically MyRecord

Also includes some instances for newtypes from @base@ such as 'Identity' and 'Const'.
-}

module Data.Group.Generics
  ( )
  where

-- base

import Data.Functor.Const
  ( Const(..) )
import Data.Functor.Identity
  ( Identity(..) )
import Data.Ord
  ( Down(..) )
import Data.Proxy
  ( Proxy(..) )
import GHC.Generics

#if __GLASGOW_HASKELL__ < 903
-- generic-data

import Generic.Data
  ( Generically(..), GenericProduct(..) )
#endif

-- groups

import Data.Group
  ( Group(..), Abelian )

-----------------------------------------------------------------------

-- Instances for 'Group'.


ginvert :: forall g. ( Generic g, Group ( Rep g () ) ) => g -> g
ginvert :: forall g. (Generic g, Group (Rep g ())) => g -> g
ginvert = forall a x. Generic a => Rep a x -> a
to forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall m. Group m => m -> m
invert @( Rep g () ) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a x. Generic a => a -> Rep a x
from

gpow :: forall n g. ( Integral n, Generic g, Group ( Rep g () ) ) => g -> n -> g
gpow :: forall n g.
(Integral n, Generic g, Group (Rep g ())) =>
g -> n -> g
gpow g
x n
n = forall a x. Generic a => Rep a x -> a
to ( forall m x. (Group m, Integral x) => m -> x -> m
pow @( Rep g () ) ( forall a x. Generic a => a -> Rep a x
from g
x ) n
n )

#if __GLASGOW_HASKELL__ < 903
instance
  ( Generic g
  , Monoid ( GenericProduct g )
  , Group  ( Rep g () )
  )
  => Group ( GenericProduct g ) where
  invert :: GenericProduct g -> GenericProduct g
invert  = forall g. (Generic g, Group (Rep g ())) => g -> g
ginvert
  pow :: forall x. Integral x => GenericProduct g -> x -> GenericProduct g
pow GenericProduct g
x x
n = forall n g.
(Integral n, Generic g, Group (Rep g ())) =>
g -> n -> g
gpow GenericProduct g
x x
n
#endif

instance
  ( Generic g
  , Monoid ( Generically g )
  , Group  ( Rep g () )
  )
  => Group ( Generically g ) where
  invert :: Generically g -> Generically g
invert (Generically g
g) = forall a. a -> Generically a
Generically ( forall g. (Generic g, Group (Rep g ())) => g -> g
ginvert g
g )
  pow :: forall x. Integral x => Generically g -> x -> Generically g
pow (Generically g
x) x
n = forall a. a -> Generically a
Generically ( forall n g.
(Integral n, Generic g, Group (Rep g ())) =>
g -> n -> g
gpow g
x x
n )

instance Group (U1 p) where
  invert :: U1 p -> U1 p
invert U1 p
_ = forall k (p :: k). U1 p
U1
  pow :: forall x. Integral x => U1 p -> x -> U1 p
pow  U1 p
_ x
_ = forall k (p :: k). U1 p
U1
instance Group (f p) => Group (Rec1 f p) where
  invert :: Rec1 f p -> Rec1 f p
invert (Rec1 f p
g) = forall k (f :: k -> *) (p :: k). f p -> Rec1 f p
Rec1 (forall m. Group m => m -> m
invert f p
g)
  pow :: forall x. Integral x => Rec1 f p -> x -> Rec1 f p
pow (Rec1 f p
g) x
n = forall k (f :: k -> *) (p :: k). f p -> Rec1 f p
Rec1 (forall m x. (Group m, Integral x) => m -> x -> m
pow f p
g x
n)
instance Group (f p) => Group (M1 i c f p) where
  invert :: M1 i c f p -> M1 i c f p
invert (M1 f p
g) = forall k i (c :: Meta) (f :: k -> *) (p :: k). f p -> M1 i c f p
M1 (forall m. Group m => m -> m
invert f p
g)
  pow :: forall x. Integral x => M1 i c f p -> x -> M1 i c f p
pow (M1 f p
g) x
n = forall k i (c :: Meta) (f :: k -> *) (p :: k). f p -> M1 i c f p
M1 (forall m x. (Group m, Integral x) => m -> x -> m
pow f p
g x
n)
instance Group g => Group (K1 i g p) where
  invert :: K1 i g p -> K1 i g p
invert (K1 g
g) = forall k i c (p :: k). c -> K1 i c p
K1 (forall m. Group m => m -> m
invert g
g)
  pow :: forall x. Integral x => K1 i g p -> x -> K1 i g p
pow (K1 g
g) x
n = forall k i c (p :: k). c -> K1 i c p
K1 (forall m x. (Group m, Integral x) => m -> x -> m
pow g
g x
n)
instance Group g => Group (Par1 g) where
  invert :: Par1 g -> Par1 g
invert (Par1 g
g) = forall p. p -> Par1 p
Par1 (forall m. Group m => m -> m
invert g
g)
  pow :: forall x. Integral x => Par1 g -> x -> Par1 g
pow (Par1 g
g) x
n = forall p. p -> Par1 p
Par1 (forall m x. (Group m, Integral x) => m -> x -> m
pow g
g x
n)

#if !MIN_VERSION_groups(0,5,0)
instance (Group (f1 p), Group (f2 p) ) => Group ((:*:) f1 f2 p) where
  invert ( g1 :*: g2 ) = ( invert g1 :*: invert g2 )
  pow ( g1 :*: g2 ) n = ( pow g1 n :*: pow g2 n )
instance Group (f (g p)) => Group ((:.:) f g p) where
  invert (Comp1 g) = Comp1 (invert g)
  pow (Comp1 g) n = Comp1 (pow g n)
instance Group a => Group (Down a) where
  invert (Down a) = Down (invert a)
  pow (Down a) n = Down (pow a n)
instance Group a => Group (Identity a) where
  invert (Identity a) = Identity (invert a)
  pow (Identity a) n = Identity (pow a n)
instance Group a => Group (Const a b) where
  invert (Const a) = Const (invert a)
  pow (Const a) n = Const (pow a n)
instance Group (Proxy s) where
  invert _ = Proxy
  pow  _ _ = Proxy
#endif

-----------------------------------------------------------------------

-- Instances for 'Abelian'.


instance
  ( Generic g
  , Group ( Generically g )
  , Abelian ( Rep g () )
  )
  => Abelian ( Generically g )

#if __GLASGOW_HASKELL__ < 903
instance
  ( Generic g
  , Monoid  ( GenericProduct g )
  , Abelian ( Rep g () )
  )
  => Abelian ( GenericProduct g )
#endif

#if !MIN_VERSION_groups(0,5,0)
instance Abelian (U1 p)
instance Abelian (f p) => Abelian (Rec1 f p)
instance Abelian (f p) => Abelian (M1 i c f p)
instance Abelian g => Abelian (K1 i g p)
instance Abelian g => Abelian (Par1 g)
instance (Abelian (f1 p), Abelian (f2 p)) => Abelian ((:*:) f1 f2 p)
instance Abelian (f (g p)) => Abelian ((:.:) f g p)

instance Abelian a => Abelian (Down a)
instance Abelian a => Abelian (Identity a) where
instance Abelian a => Abelian (Const a b) where
instance Abelian (Proxy s) where
#endif