module NumberTheory (
pythSide,
pythLeg,
pythHyp,
primPythHyp,
primPythLeg,
canon,
evalPoly,
polyCong,
exponentiate,
rsaGenKeys,
rsaEval,
units,
nilpotents,
idempotents,
roots,
almostRoots,
order,
orders,
expressAsRoots,
powerCong,
ilogBM,
divisors,
factorize,
nonUnitFactorize,
primes,
isPrime,
areCoprime,
legendre,
kronecker,
totient,
tau,
sigma,
mobius,
littleOmega,
bigOmega,
GaussInt((:+)),
real,
imag,
conjugate,
magnitude,
(.+),
(.-),
(.*),
(./),
(.%),
gIsPrime,
gPrimes,
gGCD,
gFindPrime,
gExponentiate,
gFactorize,
factorial,
fibonacci,
permute,
choose,
enumerate,
asSumOfSquares,
ContinuedFraction(Finite, Infinite),
continuedFractionFromDouble,
continuedFractionFromRational,
continuedFractionFromQuadratic,
continuedFractionToRational,
continuedFractionToFractional
) where
import Data.List ((\\), elemIndex, genericLength, nub, sort)
import qualified Data.Map as Map (fromListWith, toList)
import Data.Monoid
import qualified Data.Numbers.Primes as Primes (primes)
import Data.Ratio ((%), denominator, numerator, Ratio)
import qualified Data.Set as Set (fromList, Set, size, toList)
canon :: Integral a => a -> a -> a
canon x m
| x < 0 = canon (x + m) m
| otherwise = x `mod` m
sqrti :: Integral a => a -> Double
sqrti = (sqrt :: Double -> Double) . fromIntegral
isIntegral :: Double -> Bool
isIntegral x = (floor :: Double -> Integer) x == ceiling x
pythSide :: Integral a => a -> [(a, a, a)]
pythSide s = sort $ pythLeg s ++ pythHyp s
pythLeg :: Integral a => a -> [(a, a, a)]
pythLeg leg = sort [ (k * a, k * b, k * c)
| k <- divisors leg
, (a, b, c) <- primPythLeg $ leg `quot` k
]
primPythLeg :: Integral a => a -> [(a, a, a)]
primPythLeg leg =
sort [ (a, b, c)
| (m, n) <- findMN
, let (a, b, c) = generatePythTriple m n
]
where
findMN
| leg `mod` 2 == 0 =
[ (m, n)
| n <- divisors leg
, let m = quot leg (2 * n)
, areLegalParametersForPythTriple m n
]
| otherwise =
[ (m, n)
| n <- [1 .. leg]
, let x = sqrti $ leg + n * n
, isIntegral x
, let m = floor x
, areLegalParametersForPythTriple m n
]
pythHyp :: Integral a => a -> [(a, a, a)]
pythHyp hypotenuse = sort [ (k * a, k * b, k * c)
| k <- divisors hypotenuse
, (a, b, c) <- primPythHyp $ hypotenuse `quot` k
]
primPythHyp :: Integral a => a -> [(a, a, a)]
primPythHyp hypotenuse =
sort [ (a, b, c)
| n <- [1 .. floor $ sqrti hypotenuse]
, let x = sqrti (hypotenuse n * n)
, isIntegral x
, let m = floor x
, areLegalParametersForPythTriple m n
, let (a, b, c) = generatePythTriple m n
]
generatePythTriple :: Integral a => a -> a -> (a, a, a)
generatePythTriple m n =
let a = m * m n * n
b = 2 * m * n
c = m * m + n * n
in (a, b, c)
areLegalParametersForPythTriple :: Integral a => a -> a -> Bool
areLegalParametersForPythTriple m n =
0 < n &&
n < m &&
gcd m n == 1 &&
even (m*n)
divisors :: Integral a => a -> [a]
divisors n
| n == 0 = []
| n == 1 = [1]
| n < 0 = divisors (n)
| otherwise = let divisorPairs = [nub [x, quot n x] | x <- [2 .. limit], n `mod` x == 0]
limit = floor $ sqrti n
in sort . ([1, n] ++) $ concat divisorPairs
factorize :: (Integral a) => a -> [(a, a)]
factorize n
| n == 0 = []
| n < 0 = (1, 1) : nonUnitFactorize (n)
| otherwise = (1, 1) : nonUnitFactorize n
nonUnitFactorize :: (Integral a) => a -> [(a, a)]
nonUnitFactorize n
| n == 0 = []
| n < 0 = nonUnitFactorize (n)
| otherwise = let findFactors :: Integral a => a -> [a] -> [a]
findFactors 1 acc = sort acc
findFactors k acc =
let d = head . tail $ divisors k
in findFactors (quot k d) (d : acc)
in collapseMultiplicities $ findFactors n []
collapseMultiplicities :: (Ord a, Num b) => [a] -> [(a, b)]
collapseMultiplicities list = Map.toList (Map.fromListWith (+) [(x, 1)| x <- list])
totient :: Integral a => a -> a
totient n
| n < 0 = totient (n)
| n == 0 = 0
| n == 1 = 1
| otherwise = let primeList = primes n
offset = n ^ (genericLength primeList (1 :: Integer))
diffList = map ((`subtract` n) . quot n) primeList
in product diffList `quot` offset
primes :: Integral a => a -> [a]
primes = map fst . nonUnitFactorize
isPrime :: Integral a => a -> Bool
isPrime n = n `elem` takeWhile (<= n) (dropWhile (< n) Primes.primes)
areCoprime :: Integral a => a -> a -> Bool
areCoprime = ((1 ==) .) . gcd
evalPoly :: forall a. Integral a => a -> a -> [a] -> a
evalPoly m x cs = evalPolyHelper . reverse $ map (`canon` m) cs
where
evalPolyHelper :: [a] -> a
evalPolyHelper [] = 0
evalPolyHelper [c] = c `mod` m
evalPolyHelper (c : ct) = let val = evalPolyHelper ct
in (val * x + c) `mod` m
polyCong :: Integral a => a -> [a] -> [a]
polyCong m cs = filter (\x -> evalPoly m x cs == 0) [0 .. m 1]
exponentiate :: (Integral a) => a -> a -> a -> a
exponentiate a e m
| e < 0 && a `elem` us = exponentiate a (canon (ul + e) m) m
| e < 0 = error "a is not invertible in Z mod m"
| e == 0 = 1
| even e = canon (s * s) m
| otherwise = canon (q * a) m
where
s = exponentiate a (quot e 2) m
q = exponentiate a (e 1) m
us = units m
ul = genericLength us
type Key a = (a, a)
rsaGenKeys :: Integral a => a -> a -> [(Key a, Key a)]
rsaGenKeys p q
| not (isPrime p && isPrime q) = error "p and q must both be prime"
| otherwise =
[ ((e, n), (d, n))
| let n = p * q
phi = totient n
, e <- filter (areCoprime phi) [2 .. phi 1]
, d <- polyCong phi [e, 1]
]
rsaEval :: (Integral a) => Key a -> a -> a
rsaEval (k, n) text = exponentiate text k n
units :: Integral a => a -> [a]
units n = filter (areCoprime n) [1 .. n 1]
nilpotents :: (Integral a) => a -> [a]
nilpotents m
| r == 0 = []
| otherwise = [ n
| n <- [0 .. m 1]
, let powers = map (\e -> exponentiate n e m) [1 .. r]
, 0 `elem` powers
]
where r = genericLength $ units m
idempotents :: Integral a => a -> [a]
idempotents = flip polyCong [1, 1, 0]
roots :: (Integral a) => a -> [a]
roots m
| null us = []
| otherwise = [ u | u <- us, order u m == genericLength us]
where us = units m
almostRoots :: forall a. (Integral a) => a -> [a]
almostRoots m = let unitCount = genericLength $ units m
expList = [1 .. unitCount + 1]
generateUnits :: a -> Set.Set a
generateUnits u = Set.fromList $ concat
[ [k, canon (k) m]
| e <- expList
, let k = exponentiate u e m
]
in sort [ u
| u <- units m \\ roots m
, unitCount == (fromIntegral . Set.size $ generateUnits u)
]
order :: (Integral a) => a -> a -> a
order x m = head [ ord
| ord <- [1 .. genericLength $ units m]
, exponentiate (canon x m) ord m == 1
]
orders :: (Integral a) => a -> [a]
orders m = map (`order` m) $ units m
rootsOrAlmostRoots :: (Integral a) => a -> [a]
rootsOrAlmostRoots m =
case roots m of
[] -> almostRoots m
rs -> rs
expressAsRoots :: (Integral a) => a -> a -> [(a, a)]
expressAsRoots x m =
let rs = rootsOrAlmostRoots m
in [ (r', e)
| r <- rs
, e <- [1 .. order r m]
, let k = exponentiate r e m
, r' <- [ r | k == x ]
++ [ r | canon (k) m == x ]
]
powerCong :: (Integral a) => a -> a -> a -> [a]
powerCong e k m = [ x
| x <- [1 .. m]
, exponentiate x e m == canon k m
]
ilogBM :: (Integral a) => a -> a -> a -> [a]
ilogBM b k m = let bc = canon b m
kc = canon k m
in [ e
| e <- [1 .. order bc m]
, exponentiate bc e m == kc
]
legendre :: (Integral a) => a -> a -> a
legendre q p
| not $ isPrime p = error "p is not prime"
| p == 2 = error "p must be odd"
| otherwise = let r = exponentiate q (quot (p 1) 2) p
in if r > 1 then (1) else r
kronecker :: (Integral a) => a -> a -> a
kronecker a n
| n == (1) && a < 0 = 1
| n == (1) = 1
| n == 0 && abs a == 1 = 1
| n == 0 = 0
| n == 1 = 1
| n == 2 && even a = 0
| n == 2 && abs (a `mod` 8) == 1 = 1
| n == 2 = 1
| isPrime n = legendre a n
| otherwise = kronecker a u * product [ kronecker a p ^ e | (p, e) <- nonUnitFactorize n]
where u = if a < 0 then 1 else 1
tau :: Integral a => a -> a
tau = genericLength . divisors
sigma :: Integral a => a -> a -> a
sigma k = sum . map (^ k) . divisors
mobius :: (Integral a) => a -> a
mobius n
| isSquareFree n = (1) ^ littleOmega n
| otherwise = 0
where
isSquareFree :: Integral a => a -> Bool
isSquareFree = all (odd . snd) . nonUnitFactorize
littleOmega :: Integral a => a -> a
littleOmega = genericLength . nonUnitFactorize
bigOmega :: Integral a => a -> a
bigOmega = sum . map snd . nonUnitFactorize
infix 6 :+
data GaussInt a = a :+ a deriving (Ord, Eq)
instance (Show a, Ord a, Num a) => Show (GaussInt a) where
show (a :+ b) = show a ++ op ++ b' ++ "i"
where op = if b > 0 then "+" else "-"
b' = if abs b /= 1 then show (abs b) else ""
instance (Monoid a) => Monoid (GaussInt a) where
mempty = (mempty :: a) :+ (mempty :: a)
(c :+ d) `mappend` (e :+ f) = (c `mappend` e) :+ (d `mappend` f)
real :: GaussInt a -> a
real (x :+ _) = x
imag :: GaussInt a -> a
imag (_ :+ y) = y
conjugate :: Num a => GaussInt a -> GaussInt a
conjugate (r :+ i) = r :+ (i)
magnitude :: Num a => GaussInt a -> a
magnitude (x :+ y) = x * x + y * y
(.+) :: Num a => GaussInt a -> GaussInt a -> GaussInt a
(gr :+ gi) .+ (hr :+ hi) = (gr + hr) :+ (gi + hi)
(.-) :: Num a => GaussInt a -> GaussInt a -> GaussInt a
(gr :+ gi) .- (hr :+ hi) = (gr hr) :+ (gi hi)
(.*) :: Num a => GaussInt a -> GaussInt a -> GaussInt a
(gr :+ gi) .* (hr :+ hi) = (gr * hr hi * gi) :+ (gr * hi + gi * hr)
divToNearest :: (Integral a, Integral b) => a -> a -> b
divToNearest x y = round ((x % 1) / (y % 1))
(./) :: Integral a => GaussInt a -> GaussInt a -> GaussInt a
g ./ h =
let nr :+ ni = g .* conjugate h
denom = magnitude h
in divToNearest nr denom :+ divToNearest ni denom
(.%) :: Integral a => GaussInt a -> GaussInt a -> GaussInt a
g .% m =
let q = g ./ m
p = m .* q
in g .- p
gIsPrime :: Integral a => GaussInt a -> Bool
gIsPrime = isPrime . magnitude
gPrimes :: Integral a => [GaussInt a]
gPrimes = [ a' :+ b'
| mag <- Primes.primes
, let radius = floor $ sqrti mag
, a <- [0 .. radius]
, let y = sqrti $ mag a*a
, isIntegral y
, let b = floor y
, a' <- [a, a]
, b' <- [b, b]
]
gGCD :: Integral a => GaussInt a -> GaussInt a -> GaussInt a
gGCD g h
| h == 0 :+ 0 = g
| otherwise = gGCD h (g .% h)
gFindPrime :: (Integral a) => a -> [GaussInt a]
gFindPrime 2 = [1 :+ 1]
gFindPrime p
| p `mod` 4 == 1 && isPrime p =
let r = head $ roots p
z = exponentiate r (quot (p 1) 4) p
in [gGCD (p :+ 0) (z :+ 1)]
| otherwise = []
gExponentiate :: (Integral a) => GaussInt a -> a -> GaussInt a
gExponentiate a e
| e < 0 = error "Cannot exponentiate Gaussian Int to negative power"
| e == 0 = 1 :+ 0
| even e = s .* s
| otherwise = a .* m
where
s = gExponentiate a (quot e 2)
m = gExponentiate a (e 1)
gFactorize :: forall a. (Integral a) => GaussInt a -> [(GaussInt a, a)]
gFactorize g
| g == 0 :+ 0 = [(0 :+ 0, 1)]
| otherwise =
let nonUnits = concatMap processPrime . nonUnitFactorize $ magnitude g
nonUnitProduct = foldr ((.*) . uncurry gExponentiate) (1 :+ 0) nonUnits
remainderUnit = (g ./ nonUnitProduct, 1)
in remainderUnit : nonUnits
where
processPrime :: (a, a) -> [(GaussInt a, a)]
processPrime (p, e)
| p `mod` 4 == 3 = [(p :+ 0, quot e 2)]
| otherwise = collapseMultiplicities $ processGaussPrime g []
where
processGaussPrime :: GaussInt a -> [GaussInt a] -> [GaussInt a]
processGaussPrime g' acc = do
gp <- gFindPrime p
let fs = filter (\f -> g' .% f == 0 :+ 0) [gp, conjugate gp]
case fs of
[] -> acc
f : _ -> processGaussPrime (g' ./ f) (f : acc)
factorial :: Integral a => a -> a
factorial n = product [1 .. n]
fibonacci :: Num a => [a]
fibonacci = 0 : 1 : zipWith (+) fibonacci (tail fibonacci)
permute :: Integral a => a -> a -> a
permute n k = factorial n `quot` factorial (n k)
choose :: Integral a => a -> a -> a
choose n r = (n `permute` r) `quot` factorial r
enumerate :: [[a]] -> [[a]]
enumerate [] = [[]]
enumerate (c:cs) = [ a : as
| a <- c
, as <- enumerate cs
]
asSumOfSquares :: Integral a => a -> [(a, a)]
asSumOfSquares n = Set.toList . Set.fromList $
[ (x', y')
| x <- [1 .. floor $ sqrti n]
, let d = n x * x
, d > 0
, let sd = sqrti d
, isIntegral sd
, let y = floor sd
[x', y'] = sort [x, y]
]
data ContinuedFraction a = Finite [a] | Infinite ([a], [a])
instance (Show a) => Show (ContinuedFraction a) where
show (Finite as) = "Finite " ++ show as
show (Infinite (as, ps)) = "Infinite " ++ show as ++ show ps ++ "..."
continuedFractionFromDouble :: forall a. (Integral a) => Double -> a -> ContinuedFraction a
continuedFractionFromDouble x precision
| precision < 1 = Finite []
| otherwise =
let ts = getTs (fractionalPart x) precision
in Finite $ integralPart x : map (integralPart . recip) (filter (/= 0) ts)
where
integralPart :: Double -> a
integralPart n = fst $ (properFraction :: Double -> (a, Double)) n
fractionalPart :: Double -> Double
fractionalPart 0 = 0
fractionalPart n = snd $ (properFraction :: Double -> (Integer, Double)) n
getTs :: Integral a => Double -> a -> [Double]
getTs y n = reverse $ tRunner [y] n
where
tRunner [] _ = error "improper call of tRunner. This should never happen."
tRunner ts 0 = ts
tRunner ts@(t : _) m
| tn == 0 = ts
| otherwise = tRunner (tn : ts) (m 1)
where tn = fractionalPart $ recip t
continuedFractionFromQuadratic :: forall a. (Integral a) => a -> a -> a -> ContinuedFraction a
continuedFractionFromQuadratic m0 d q0
| q0 == 0 = error "Cannot divide by 0"
| isIntegral $ sqrti d = continuedFractionFromRational ((m0 + (floor . sqrti $ d)) % q0)
| not . isIntegral $ getNextQ m0 q0 = continuedFractionFromQuadratic (m0 * q0) (d * q0 * q0) (q0 * q0)
| otherwise =
let a0 = truncate $ (fromIntegral m0 + sqrti d) / fromIntegral q0
in helper [(m0, q0, a0)]
where
helper :: [(a, a, a)] -> ContinuedFraction a
helper [] = error "improper call to helper function. This will never happen."
helper ts@((mp, qp, ap) : _) =
let mn = ap * qp mp
qn = (truncate :: Double -> a) $ getNextQ mn qp
an = truncate ((fromIntegral mn + sqrti d) / fromIntegral qn)
ts' = reverse ts
as' = map third ts'
in case elemIndex (mn, qn, an) ts' of
Just idx -> Infinite (take idx as', drop idx as')
Nothing -> helper $ (mn, qn, an) : ts
getNextQ :: a -> a -> Double
getNextQ mp qp = fromIntegral (d mp * mp) / fromIntegral qp
third :: (a, b, c) -> c
third (_, _, x) = x
continuedFractionToRational :: (Integral a) => ContinuedFraction a -> Ratio a
continuedFractionToRational frac =
let list = case frac of
Finite as -> as
Infinite (as, periods) -> as ++ take 35 (cycle periods)
in foldr (\ai rat -> (ai % 1) + (1 / rat)) (last list % 1) (init list)
continuedFractionFromRational :: Integral a => Ratio a -> ContinuedFraction a
continuedFractionFromRational rat
| denominator rat == 1 = Finite [numerator rat]
| numerator fracPart == 1 = Finite [intPart, denominator fracPart]
| otherwise =
let Finite trail = continuedFractionFromRational (1 / fracPart)
in Finite (intPart : trail)
where
intPart = numerator rat `div` denominator rat
fracPart = rat (intPart % 1)
continuedFractionToFractional :: (Fractional a) => ContinuedFraction Integer -> a
continuedFractionToFractional = fromRational . continuedFractionToRational