module Data.Field.Galois.Prime
( Prime
, PrimeField
, fromP
, toP
) where
import Protolude as P hiding (Semiring, natVal, rem)
import Control.Monad.Random (Random(..))
import Data.Euclidean as S (Euclidean(..), GcdDomain)
import Data.Field (Field)
import Data.Group (Group(..))
import Data.Mod (Mod, unMod, (^%))
import Data.Semiring (Ring(..), Semiring(..))
import GHC.Natural (naturalToInteger)
import GHC.TypeNats (natVal)
import Test.QuickCheck (Arbitrary(..), choose)
import Text.PrettyPrint.Leijen.Text (Pretty(..))
import Data.Field.Galois.Base (GaloisField(..))
class GaloisField k => PrimeField k where
{-# MINIMAL fromP #-}
fromP :: k -> Integer
newtype Prime (p :: Nat) = P (Mod p)
deriving (Eq, Ord, Show, Generic, Num, Fractional, Euclidean, Field, GcdDomain, Ring, Semiring, Bounded, Enum, NFData)
instance Hashable (Prime p) where
hashWithSalt s (P x) = hashWithSalt s (unMod x)
instance KnownNat p => PrimeField (Prime p) where
fromP (P x) = naturalToInteger (unMod x)
{-# INLINABLE fromP #-}
instance KnownNat p => GaloisField (Prime p) where
char = natVal
{-# INLINABLE char #-}
deg = const 1
{-# INLINABLE deg #-}
frob = identity
{-# INLINABLE frob #-}
{-# RULES "Prime.pow"
forall (k :: KnownNat p => Prime p) n . (^) k n = pow k n
#-}
instance KnownNat p => Group (Prime p) where
invert = recip
{-# INLINE invert #-}
pow (P x) k = P (x ^% k)
{-# INLINE pow #-}
instance KnownNat p => Monoid (Prime p) where
mempty = P 1
{-# INLINE mempty #-}
instance KnownNat p => Semigroup (Prime p) where
(<>) = (*)
{-# INLINE (<>) #-}
stimes = flip pow
{-# INLINE stimes #-}
instance KnownNat p => Arbitrary (Prime p) where
arbitrary = choose (minBound, maxBound)
{-# INLINABLE arbitrary #-}
instance KnownNat p => Integral (Prime p) where
quotRem = S.quotRem
{-# INLINE quotRem #-}
toInteger = fromP
{-# INLINABLE toInteger #-}
instance KnownNat p => Pretty (Prime p) where
pretty (P x) = pretty $ naturalToInteger $ unMod x
instance KnownNat p => Random (Prime p) where
random = randomR (minBound, maxBound)
{-# INLINABLE random #-}
randomR (a, b) = first fromInteger . randomR (fromP a, fromP b)
{-# INLINABLE randomR #-}
instance KnownNat p => Real (Prime p) where
toRational = fromIntegral
{-# INLINABLE toRational #-}
toP :: KnownNat p => Integer -> Prime p
toP = fromInteger
{-# INLINABLE toP #-}