-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #130 from b-mehta/dirichlet-characters
Discrete log
- Loading branch information
Showing
13 changed files
with
333 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
-- | | ||
-- Module: Math.NumberTheory.Moduli.DiscreteLogarithm | ||
-- Copyright: (c) 2018 Bhavik Mehta | ||
-- License: MIT | ||
-- Maintainer: Andrew Lelechenko <[email protected]> | ||
-- Stability: Provisional | ||
-- Portability: Non-portable | ||
-- | ||
|
||
{-# LANGUAGE ViewPatterns #-} | ||
{-# LANGUAGE BangPatterns #-} | ||
|
||
module Math.NumberTheory.Moduli.DiscreteLogarithm | ||
( discreteLogarithm | ||
) where | ||
|
||
import qualified Data.IntMap.Strict as M | ||
import Data.Maybe (maybeToList) | ||
import Numeric.Natural (Natural) | ||
import GHC.Integer.GMP.Internals (recipModInteger, powModInteger) | ||
|
||
import Math.NumberTheory.Moduli.Chinese (chineseRemainder2) | ||
import Math.NumberTheory.Moduli.Class (KnownNat, MultMod(..), Mod, getVal) | ||
import Math.NumberTheory.Moduli.Equations (solveLinear') | ||
import Math.NumberTheory.Moduli.PrimitiveRoot (PrimitiveRoot(..), CyclicGroup(..)) | ||
import Math.NumberTheory.Powers.Squares (integerSquareRoot) | ||
import Math.NumberTheory.UniqueFactorisation (unPrime) | ||
|
||
-- | Computes the discrete logarithm. Currently uses a combination of the baby-step | ||
-- giant-step method and Pollard's rho algorithm, with Bach reduction. | ||
discreteLogarithm :: KnownNat m => PrimitiveRoot m -> MultMod m -> Natural | ||
discreteLogarithm a b = discreteLogarithm' (getGroup a) (multElement $ unPrimitiveRoot a) (multElement b) | ||
|
||
discreteLogarithm' | ||
:: KnownNat m | ||
=> CyclicGroup Natural -- ^ group structure (must be the multiplicative group mod m) | ||
-> Mod m -- ^ a | ||
-> Mod m -- ^ b | ||
-> Natural -- ^ result | ||
discreteLogarithm' cg a b = | ||
case cg of | ||
CG2 -> 0 | ||
-- the only valid input was a=1, b=1 | ||
CG4 -> if b == 1 then 0 else 1 | ||
-- the only possible input here is a=3 with b = 1 or 3 | ||
CGOddPrimePower (unPrime -> p) k -> discreteLogarithmPP p k (getVal a) (getVal b) | ||
CGDoubleOddPrimePower (unPrime -> p) k -> discreteLogarithmPP p k (getVal a `rem` p^k) (getVal b `rem` p^k) | ||
-- we have the isomorphism t -> t `rem` p^k from (Z/2p^kZ)* -> (Z/p^kZ)* | ||
|
||
-- Implementation of Bach reduction (https://www2.eecs.berkeley.edu/Pubs/TechRpts/1984/CSD-84-186.pdf) | ||
{-# INLINE discreteLogarithmPP #-} | ||
discreteLogarithmPP :: Integer -> Word -> Integer -> Integer -> Natural | ||
discreteLogarithmPP p 1 a b = discreteLogarithmPrime p a b | ||
discreteLogarithmPP p k a b = fromInteger result | ||
where | ||
baseSol = toInteger $ discreteLogarithmPrime p (a `rem` p) (b `rem` p) | ||
thetaA = theta p pkMinusOne a | ||
thetaB = theta p pkMinusOne b | ||
pkMinusOne = p^(k-1) | ||
c = (recipModInteger thetaA pkMinusOne * thetaB) `rem` pkMinusOne | ||
result = chineseRemainder2 (baseSol, p-1) (c, pkMinusOne) | ||
|
||
-- compute the homomorphism theta given in https://math.stackexchange.com/a/1864495/418148 | ||
{-# INLINE theta #-} | ||
theta :: Integer -> Integer -> Integer -> Integer | ||
theta p pkMinusOne a = (numerator `quot` pk) `rem` pkMinusOne | ||
where | ||
pk = pkMinusOne * p | ||
p2kMinusOne = pkMinusOne * pk | ||
numerator = (powModInteger a (pk - pkMinusOne) p2kMinusOne - 1) `rem` p2kMinusOne | ||
|
||
-- TODO: Use Pollig-Hellman to reduce the problem further into groups of prime order. | ||
-- While Bach reduction simplifies the problem into groups of the form (Z/pZ)*, these | ||
-- have non-prime order, and the Pollig-Hellman algorithm can reduce the problem into | ||
-- smaller groups of prime order. | ||
-- In addition, the gcd check before solveLinear is applied in Pollard below will be | ||
-- made redundant, since n would be prime. | ||
discreteLogarithmPrime :: Integer -> Integer -> Integer -> Natural | ||
discreteLogarithmPrime p a b | ||
| p < 100000000 = fromIntegral $ discreteLogarithmPrimeBSGS (fromInteger p) (fromInteger a) (fromInteger b) | ||
| otherwise = discreteLogarithmPrimePollard p a b | ||
|
||
discreteLogarithmPrimeBSGS :: Int -> Int -> Int -> Int | ||
discreteLogarithmPrimeBSGS p a b = head [i*m + j | (v,i) <- zip giants [0..m-1], j <- maybeToList (M.lookup v table)] | ||
where | ||
m = integerSquareRoot (p - 2) + 1 -- simple way of ceiling (sqrt (p-1)) | ||
babies = iterate (.* a) 1 | ||
table = M.fromList (zip babies [0..m-1]) | ||
aInv = recipModInteger (toInteger a) (toInteger p) | ||
bigGiant = fromInteger $ powModInteger aInv (toInteger m) (toInteger p) | ||
giants = iterate (.* bigGiant) b | ||
x .* y = x * y `rem` p | ||
|
||
-- TODO: Use more advanced walks, in order to reduce divisions, cf | ||
-- https://maths-people.anu.edu.au/~brent/pd/rpb231.pdf | ||
-- This will slightly improve the expected time to collision, and can reduce the | ||
-- number of divisions performed. | ||
discreteLogarithmPrimePollard :: Integer -> Integer -> Integer -> Natural | ||
discreteLogarithmPrimePollard p a b = | ||
case concatMap runPollard [(x,y) | x <- [0..n], y <- [0..n]] of | ||
(t:_) -> fromInteger t | ||
[] -> error ("discreteLogarithm: pollard's rho failed, please report this as a bug. inputs " ++ show [p,a,b]) | ||
where | ||
n = p-1 -- order of the cyclic group | ||
halfN = n `quot` 2 | ||
mul2 m = if m < halfN then m * 2 else m * 2 - n | ||
sqrtN = integerSquareRoot n | ||
step (xi,!ai,!bi) = case xi `rem` 3 of | ||
0 -> (xi*xi `rem` p, mul2 ai, mul2 bi) | ||
1 -> ( a*xi `rem` p, ai+1, bi) | ||
_ -> ( b*xi `rem` p, ai, bi+1) | ||
initialise (x,y) = (powModInteger a x n * powModInteger b y n `rem` n, x, y) | ||
begin t = go (step t) (step (step t)) | ||
check t = powModInteger a t p == b | ||
go tort@(xi,ai,bi) hare@(x2i,a2i,b2i) | ||
| xi == x2i, gcd (bi - b2i) n < sqrtN = solveLinear' n (bi - b2i) (ai - a2i) | ||
| xi == x2i = [] | ||
| otherwise = go (step tort) (step (step hare)) | ||
runPollard = filter check . begin . initialise |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ | |
|
||
module Math.NumberTheory.Moduli.Equations | ||
( solveLinear | ||
, solveLinear' | ||
, solveQuadratic | ||
) where | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.