module Factory.Math.MultiplicativeOrder(
multiplicativeOrder
) where
import qualified Control.DeepSeq
import qualified Factory.Data.Exponential as Data.Exponential
import qualified Factory.Math.Power as Math.Power
import qualified Factory.Math.Primality as Math.Primality
import qualified Factory.Math.PrimeFactorisation as Math.PrimeFactorisation
multiplicativeOrder :: (Math.PrimeFactorisation.Algorithmic primeFactorisationAlgorithm, Control.DeepSeq.NFData i, Integral i, Show i)
=> primeFactorisationAlgorithm
-> i
-> i
-> i
multiplicativeOrder primeFactorisationAlgorithm base modulus
| modulus < 2 = error $ "Factory.Math.MultiplicativeOrder.multiplicativeOrder:\tinvalid modulus; " ++ show modulus
| not $ Math.Primality.areCoprime base modulus = error $ "Factory.Math.MultiplicativeOrder.multiplicativeOrder:\targuments aren't coprime; " ++ show (base, modulus)
| otherwise = foldr (lcm . multiplicativeOrder') 1 $ Math.PrimeFactorisation.primeFactors primeFactorisationAlgorithm modulus
where
multiplicativeOrder' e = product . map (
\e' -> let
d :: Int
d = length . takeWhile (/= 1) . iterate (
\y -> Math.Power.raiseModulo y (Data.Exponential.getBase e') pk
) $ Math.Power.raiseModulo base (totient `div` Data.Exponential.evaluate e') pk
in Data.Exponential.getBase e' ^ d
) $ Math.PrimeFactorisation.primeFactors primeFactorisationAlgorithm totient where
pk = Data.Exponential.evaluate e
totient = Math.PrimeFactorisation.primePowerTotient e