{-# LANGUAGE DataKinds, DerivingStrategies, FlexibleContexts     #-}
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PolyKinds #-}
{-# LANGUAGE RankNTypes, ScopedTypeVariables, TypeApplications   #-}
{-# LANGUAGE TypeFamilies, UndecidableInstances                  #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Algebra.Field.Finite
  ( FiniteField(..), order,
    ViaElements(..)
  ) where
import           Control.Monad.Random              (Random (..), uniform)
import           Control.Monad.Trans.Random.Strict (runRand)
import           Data.Coerce                       (coerce)
import           Data.Proxy                        (Proxy (Proxy))
import           Numeric.Algebra                   (Field, Natural, char)
import           Numeric.Rig.Characteristic        (Characteristic)
import           Prelude                           (($))
import qualified Prelude                           as P

-- | Abstract class for finite fields.
class (Field k, Characteristic k) => FiniteField k where
  power :: proxy k -> Natural
  elements :: proxy k -> [k]

order :: FiniteField k => proxy k -> Natural
order :: proxy k -> Natural
order proxy k
p = proxy k -> Natural
forall r (proxy :: * -> *). Characteristic r => proxy r -> Natural
char proxy k
p Natural -> Natural -> Natural
forall a b. (Num a, Integral b) => a -> b -> a
P.^ proxy k -> Natural
forall k (proxy :: * -> *). FiniteField k => proxy k -> Natural
power proxy k
p
{-# INLINE order #-}

newtype ViaElements k = ViaElements { ViaElements k -> k
runViaElements :: k }

instance FiniteField k => Random (ViaElements k) where
  random :: g -> (ViaElements k, g)
random = Rand g (ViaElements k) -> g -> (ViaElements k, g)
forall g a. Rand g a -> g -> (a, g)
runRand
    (Rand g (ViaElements k) -> g -> (ViaElements k, g))
-> Rand g (ViaElements k) -> g -> (ViaElements k, g)
forall a b. (a -> b) -> a -> b
$ [ViaElements k] -> Rand g (ViaElements k)
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, MonadRandom m) =>
t a -> m a
uniform ([ViaElements k] -> Rand g (ViaElements k))
-> [ViaElements k] -> Rand g (ViaElements k)
forall a b. (a -> b) -> a -> b
$ Coercible [k] [ViaElements k] => [k] -> [ViaElements k]
coerce @_ @[ViaElements k]
    ([k] -> [ViaElements k]) -> [k] -> [ViaElements k]
forall a b. (a -> b) -> a -> b
$ Proxy k -> [k]
forall k (proxy :: * -> *). FiniteField k => proxy k -> [k]
elements (Proxy k
forall k (t :: k). Proxy t
Proxy :: Proxy k)
  {-# INLINE random #-}
  randomR :: (ViaElements k, ViaElements k) -> g -> (ViaElements k, g)
randomR = [Char] -> (ViaElements k, ViaElements k) -> g -> (ViaElements k, g)
forall a. HasCallStack => [Char] -> a
P.error [Char]
"randomR: general finite fields not supported"
  {-# INLINE randomR #-}