arithmoi-0.9.0.0: Math/NumberTheory/Powers/Modular.hs
-- |
-- Module: Math.NumberTheory.Powers.Modular
-- Copyright: (c) 2017 Andrew Lelechenko
-- Licence: MIT
-- Maintainer: Andrew Lelechenko <andrew.lelechenko@gmail.com>
--
-- Modular powers (a. k. a. modular exponentiation).
--
{-# LANGUAGE CPP #-}
{-# LANGUAGE MagicHash #-}
module Math.NumberTheory.Powers.Modular
( powMod
, powModWord
, powModInt
) where
import qualified GHC.Integer.GMP.Internals as GMP (powModInteger)
import GHC.Exts (Word(..))
import GHC.Natural (powModNatural)
import qualified GHC.Integer.GMP.Internals as GMP (powModWord)
import Math.NumberTheory.Utils.FromIntegral
-- | @powMod@ @b@ @e@ @m@ computes (@b^e@) \`mod\` @m@ in effective way.
-- An exponent @e@ must be non-negative, a modulo @m@ must be positive.
-- Otherwise the behaviour of @powMod@ is undefined.
--
-- >>> powMod 2 3 5
-- 3
-- >>> powMod 3 12345678901234567890 1001
-- 1
--
-- See also 'Math.NumberTheory.Moduli.Class.powMod' and 'Math.NumberTheory.Moduli.Class.powSomeMod'.
--
-- For finite numeric types ('Int', 'Word', etc.)
-- modulo @m@ should be such that @m^2@ does not overflow,
-- otherwise the behaviour is undefined. If you
-- need both to fit into machine word and to handle large moduli,
-- take a look at 'powModInt' and 'powModWord'.
--
-- >>> powMod 3 101 (2^60-1 :: Integer)
-- 1018105167100379328 -- correct
-- >>> powMod 3 101 (2^60-1 :: Int)
-- 1115647832265427613 -- incorrect due to overflow
-- >>> powModInt 3 101 (2^60-1 :: Int)
-- 1018105167100379328 -- correct
powMod :: (Integral a, Integral b) => a -> b -> a -> a
powMod x y m
| m <= 0 = error "powModInt: non-positive modulo"
| y < 0 = error "powModInt: negative exponent"
| otherwise = f (x `rem` m) y 1 `mod` m
where
f _ 0 acc = acc
f b e acc = f (b * b `rem` m) (e `quot` 2)
(if odd e then (b * acc `rem` m) else acc)
{-# INLINE [1] powMod #-}
{-# RULES
"powMod/Integer" powMod = powModInteger
#-}
-- Work around https://ghc.haskell.org/trac/ghc/ticket/14085
powModInteger :: Integer -> Integer -> Integer -> Integer
powModInteger b e m = GMP.powModInteger (b `mod` m) e m
{-# RULES
"powMod/Natural" powMod = powModNatural
"powMod/Word" powMod = powModWord
"powMod/Int" powMod = powModInt
#-}
-- | Specialised version of 'powMod', able to handle large moduli correctly.
--
-- >>> powModWord 3 101 (2^60-1)
-- 1018105167100379328
powModWord :: Word -> Word -> Word -> Word
powModWord (W# x) (W# y) (W# m) = W# (GMP.powModWord x y m)
-- | Specialised version of 'powMod', able to handle large moduli correctly.
--
-- >> powModInt 3 101 (2^60-1)
-- 1018105167100379328
powModInt :: Int -> Int -> Int -> Int
powModInt x y m
| m <= 0 = error "powModInt: non-positive modulo"
| y < 0 = error "powModInt: negative exponent"
| otherwise = wordToInt $ powModWord (intToWord (x `mod` m)) (intToWord y) (intToWord m)