{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Math.NumberTheory.ArithmeticFunctions.Inverse
( inverseTotient
, inverseSigma
,
MinWord(..)
, MaxWord(..)
, MinNatural(..)
, MaxNatural(..)
,
asSetOfPreimages
) where
import Prelude hiding (rem, quot)
import Data.List
import Data.Map (Map)
import qualified Data.Map as M
import Data.Maybe
import Data.Ord (Down(..))
import Data.Semigroup
import Data.Semiring (Semiring(..))
import Data.Set (Set)
import qualified Data.Set as S
import Numeric.Natural
import Math.NumberTheory.ArithmeticFunctions
import Math.NumberTheory.Euclidean
import Math.NumberTheory.Logarithms
import Math.NumberTheory.Powers
import Math.NumberTheory.Primes
import Math.NumberTheory.Primes.Sieve (primes)
import Math.NumberTheory.Utils.DirichletSeries (DirichletSeries)
import qualified Math.NumberTheory.Utils.DirichletSeries as DS
import Math.NumberTheory.Utils.FromIntegral
data PrimePowers a = PrimePowers
{ _ppPrime :: Prime a
, _ppPowers :: [Word]
}
instance Show a => Show (PrimePowers a) where
show (PrimePowers p xs) = "PP " ++ show (unPrime p) ++ " " ++ show xs
atomicSeries
:: Num a
=> (a -> b)
-> ArithmeticFunction a c
-> PrimePowers a
-> DirichletSeries c b
atomicSeries point (ArithmeticFunction f g) (PrimePowers p ks) =
DS.fromDistinctAscList (map (\k -> (g (f p k), point (unPrime p ^ k))) ks)
invTotient
:: forall a. (UniqueFactorisation a, Eq a)
=> [(Prime a, Word)]
-> [PrimePowers a]
invTotient fs = map (\p -> PrimePowers p (doPrime p)) ps
where
divs :: [a]
divs = runFunctionOnFactors divisorsListA fs
ps :: [Prime a]
ps = mapMaybe (isPrime . (+ 1)) divs
doPrime :: Prime a -> [Word]
doPrime p = case lookup p fs of
Nothing -> [1]
Just k -> [1 .. k+1]
invSigma
:: forall a. (Euclidean a, Integral a, UniqueFactorisation a)
=> [(Prime a, Word)]
-> [PrimePowers a]
invSigma fs
= map (\(x, ys) -> PrimePowers x (S.toList ys))
$ M.assocs
$ M.unionWith (<>) pksSmall pksLarge
where
numDivs :: a
numDivs = runFunctionOnFactors tauA fs
divs :: [a]
divs = runFunctionOnFactors divisorsListA fs
n :: a
n = product $ map (\(p, k) -> unPrime p ^ k) fs
lim :: a
lim = numDivs `max` 2
pksSmall :: Map (Prime a) (Set Word)
pksSmall = M.fromDistinctAscList
[ (p, pows)
| p <- takeWhile ((< lim) . unPrime) primes
, let pows = doPrime p
, not (null pows)
]
doPrime :: Prime a -> Set Word
doPrime p' = let p = unPrime p' in S.fromDistinctAscList
[ e
| e <- [1 .. intToWord (integerLogBase (toInteger p) (toInteger n))]
, n `rem` ((p ^ (e + 1) - 1) `quot` (p - 1)) == 0
]
pksLarge :: Map (Prime a) (Set Word)
pksLarge = M.unionsWith (<>)
[ maybe mempty (flip M.singleton (S.singleton e)) (isPrime p)
| d <- divs
, e <- [1 .. intToWord (integerLogBase (toInteger lim) (toInteger d))]
, let p = integerRoot e (d - 1)
, p ^ (e + 1) - 1 == d * (p - 1)
]
strategy
:: forall a c. (Euclidean c, Ord c)
=> ArithmeticFunction a c
-> [(Prime c, Word)]
-> [PrimePowers a]
-> [(Maybe (Prime c, Word), [PrimePowers a])]
strategy (ArithmeticFunction f g) factors args = (Nothing, ret) : rets
where
(ret, rets)
= mapAccumL go args
$ sortOn (Down . fst) factors
go
:: [PrimePowers a]
-> (Prime c, Word)
-> ([PrimePowers a], (Maybe (Prime c, Word), [PrimePowers a]))
go ts (p, k) = (rs, (Just (p, k), qs))
where
predicate (PrimePowers q ls) = any (\l -> g (f q l) `rem` unPrime p == 0) ls
(qs, rs) = partition predicate ts
invertFunction
:: forall a b c.
(Num a, Semiring b, Euclidean c, UniqueFactorisation c, Ord c)
=> (a -> b)
-> ArithmeticFunction a c
-> ([(Prime c, Word)] -> [PrimePowers a])
-> c
-> b
invertFunction point f invF n
= DS.lookup n
$ foldl' (\ds b -> uncurry processBatch b ds) (DS.fromDistinctAscList []) batches
where
factors = factorise n
batches = strategy f factors $ invF factors
processBatch
:: Maybe (Prime c, Word)
-> [PrimePowers a]
-> DirichletSeries c b
-> DirichletSeries c b
processBatch Nothing xs acc
= foldl' (DS.timesAndCrop n) acc
$ map (atomicSeries point f) xs
processBatch (Just (p, 1)) xs acc
= DS.filter (\a -> a `rem` unPrime p == 0)
$ foldl' (DS.timesAndCrop n) acc'
$ map (atomicSeries point f) xs2
where
(xs1, xs2) = partition (\(PrimePowers _ ts) -> length ts == 1) xs
vs = DS.unions $ map (atomicSeries point f) xs1
(ys, zs) = DS.partition (\a -> a `rem` unPrime p == 0) acc
acc' = ys `DS.union` DS.timesAndCrop n zs vs
processBatch (Just pk) xs acc
= (\(p, k) -> DS.filter (\a -> a `rem` (unPrime p ^ k) == 0)) pk
$ foldl' (DS.timesAndCrop n) acc
$ map (atomicSeries point f) xs
{-# SPECIALISE invertFunction :: Semiring b => (Int -> b) -> ArithmeticFunction Int Int -> ([(Prime Int, Word)] -> [PrimePowers Int]) -> Int -> b #-}
{-# SPECIALISE invertFunction :: Semiring b => (Word -> b) -> ArithmeticFunction Word Word -> ([(Prime Word, Word)] -> [PrimePowers Word]) -> Word -> b #-}
{-# SPECIALISE invertFunction :: Semiring b => (Integer -> b) -> ArithmeticFunction Integer Integer -> ([(Prime Integer, Word)] -> [PrimePowers Integer]) -> Integer -> b #-}
{-# SPECIALISE invertFunction :: Semiring b => (Natural -> b) -> ArithmeticFunction Natural Natural -> ([(Prime Natural, Word)] -> [PrimePowers Natural]) -> Natural -> b #-}
inverseTotient
:: (Semiring b, Euclidean a, UniqueFactorisation a, Ord a)
=> (a -> b)
-> a
-> b
inverseTotient point = invertFunction point totientA invTotient
{-# SPECIALISE inverseTotient :: Semiring b => (Int -> b) -> Int -> b #-}
{-# SPECIALISE inverseTotient :: Semiring b => (Word -> b) -> Word -> b #-}
{-# SPECIALISE inverseTotient :: Semiring b => (Integer -> b) -> Integer -> b #-}
{-# SPECIALISE inverseTotient :: Semiring b => (Natural -> b) -> Natural -> b #-}
inverseSigma
:: (Semiring b, Euclidean a, UniqueFactorisation a, Integral a)
=> (a -> b)
-> a
-> b
inverseSigma point = invertFunction point (sigmaA 1) invSigma
{-# SPECIALISE inverseSigma :: Semiring b => (Int -> b) -> Int -> b #-}
{-# SPECIALISE inverseSigma :: Semiring b => (Word -> b) -> Word -> b #-}
{-# SPECIALISE inverseSigma :: Semiring b => (Integer -> b) -> Integer -> b #-}
{-# SPECIALISE inverseSigma :: Semiring b => (Natural -> b) -> Natural -> b #-}
newtype MaxWord = MaxWord { unMaxWord :: Word }
deriving (Eq, Ord, Show)
instance Semiring MaxWord where
zero = MaxWord minBound
one = MaxWord 1
plus (MaxWord a) (MaxWord b) = MaxWord (a `max` b)
times (MaxWord a) (MaxWord b) = MaxWord (a * b)
newtype MinWord = MinWord { unMinWord :: Word }
deriving (Eq, Ord, Show)
instance Semiring MinWord where
zero = MinWord maxBound
one = MinWord 1
plus (MinWord a) (MinWord b) = MinWord (a `min` b)
times (MinWord a) (MinWord b) = MinWord (a * b)
newtype MaxNatural = MaxNatural { unMaxNatural :: Natural }
deriving (Eq, Ord, Show)
instance Semiring MaxNatural where
zero = MaxNatural 0
one = MaxNatural 1
plus (MaxNatural a) (MaxNatural b) = MaxNatural (a `max` b)
times (MaxNatural a) (MaxNatural b) = MaxNatural (a * b)
data MinNatural
= MinNatural { unMinNatural :: !Natural }
| Infinity
deriving (Eq, Ord, Show)
instance Semiring MinNatural where
zero = Infinity
one = MinNatural 1
plus a b = a `min` b
times Infinity _ = Infinity
times _ Infinity = Infinity
times (MinNatural a) (MinNatural b) = MinNatural (a * b)
asSetOfPreimages
:: (Euclidean a, Integral a)
=> (forall b. Semiring b => (a -> b) -> a -> b)
-> a
-> S.Set a
asSetOfPreimages f = S.mapMonotonic getProduct . f (S.singleton . Product)