-- | -- Module: Math.NumberTheory.Canon.Internals -- Copyright: (c) 2015-2019 Frederick Schneider -- Licence: MIT -- Maintainer: Frederick Schneider <fws.nyc@gmail.com> -- Stability: Provisional -- -- This module defines the internal canonical representation of numbers (CR_), a product of pairs (prime and exponent). -- PLEASE NOTE: It's not meant to be called directly. The Canon and SimpleCanon modules should be used instead. {-# LANGUAGE PatternSynonyms, ViewPatterns, ScopedTypeVariables, DataKinds, RankNTypes #-} module Math.NumberTheory.Canon.Internals ( CanonRep_, CR_, CanonElement_, crValidIntegral, crValidIntegralViaUserFunc, crValidRational, crValidRationalViaUserFunc, crExp, crRoot, crMaxRoot, crShow, ceShow, crFromInteger, crFromI, crToInteger, crToI, crCmp, crMult, crNegate, crAbs, crDivStrict, crSignum, crNumer, crDenom, crSplit, crDivRational, crIntegral, crShowRational, crToRational, crGCD, crLCM, crNegative, crPositive, crLog, crLogDouble, crDiv, crRadical, integerApply, crSimpleApply, crPrime, crHasSquare, crRecip, crMin, crMax, crValid, crMod, crModI, crSimplified, crDivisors, crNumDivisors, crWhichDivisor, crNthDivisor, crDivsPlus, crTau, crTotient, crPhi, crN1, cr0, cr1, creN1, crFactCutoff, crSmallFactCutoff, pattern PZero, pattern PZeroBad, pattern POne, pattern PNeg, pattern PNotPos, pattern PN1, pattern PIntNeg, pattern PIntNPos, pattern PIntPos, -- Functions deprecated from arithmoi that needed to be included here totient, pmI -- Short for powerModInteger ) where {- Canon or canon rep is short for canonical representation. In this context, it refers to the prime factorization of a number. Example: 250 = 2 * 5^3. It would be represented internally here as: [(2,1),(5,3)] So, this library along with Canon.hs can be used as shorthand for extremely large numbers. Multiplicative actions are cheap. But, addition and subtraction is potentially very expensive as any "raw" sums or differences are factorized. There are optimizations for sums/differences including those with special forms (algebraic, Aurifeuillean). Here are the possibilities: Zero: [(0,1)] One [] Other Positive Numbers: A set of (p,e) pairs where the p(rime)s are in ascending order. For "integers", the e(xponents) must be positive For "rationals", the e(xponents) must not be zero All "integers" are "rationals" but not vice-versa. Negative Numbers: (-1,1):P where P is a canon rep for any positive number Note: Much of the code is spent on handling these special cases (Zero, One, Negatives). Each integer and rational will have a unique "canon rep". Caveats: The behavior is undefined when directly working with "canon reps" which are not valid. The behavior of using rational CRs directly (when integral CRs are specified) is also undefined. The Canon library should be used as it hides these internal details. -} import Data.List (intersperse, partition) import Math.NumberTheory.Primes.Factorisation (smallFactors, stdGenFactorisation, factorise') import System.Random (mkStdGen) import Data.Bits (xor) import Data.List (sortBy) import Math.NumberTheory.Primes.Testing (isPrime) import GHC.Real (Ratio(..)) -- | Canon element: prime and exponent pair type CanonElement_ = (Integer,Integer) -- | Canonical representation: list of canon elements type CanonRep_ = [CanonElement_] -- | Shorthand for canonical representation type CR_ = CanonRep_ -- | Pattern to match the CR_ equivalent of 1 pattern POne :: forall t. [t] pattern POne = [] -- | Pattern to match the CR_ equivalent of zero pattern PZero :: forall a a1. (Num a, Num a1, Eq a, Eq a1) => [(a1, a)] pattern PZero = [(0,1)] -- | Pattern to match the CR_ equivalent of -1 pattern PN1 :: forall a a1. (Num a, Num a1, Eq a, Eq a1) => [(a1, a)] pattern PN1 = [(-1,1)] -- | Pattern to match a badly formed zero, meaning it's an invalid CR_ pattern PZeroBad :: forall t a. (Num a, Eq a) => [(a, t)] pattern PZeroBad <- ((0,_):_) -- MUST check after PZero -- No longer necessary -- pattern PNotIntegral :: forall a t. (Num a, Ord a) => [(t, a)] -- pattern PNotIntegral <- ( (_, compare 0 -> GT):_ ) -- negative exponent in the 2nd member of pair -- | Pattern to match a non-positive CR_ pattern PNotPos :: forall t a. (Num a, Ord a) => [(a, t)] pattern PNotPos <- ( (compare 1 -> GT, _):_ ) -- first term is 0, -1 and so not positive -- | Pattern to match a negative number pattern PIntNeg :: forall a. (Num a, Ord a) => a pattern PIntNeg <- (compare 0 -> GT) -- | Pattern to match a positive number pattern PIntPos :: forall a. (Num a, Ord a) => a pattern PIntPos <- (compare 0 -> LT) -- | Pattern to match a non-positive number pattern PIntNPos :: forall a. (Num a, Ord a) => a pattern PIntNPos <- (compare 1 -> GT) -- | Canonical values for a few special numbers creN1, cre0 :: CanonElement_ creN1 = (-1,1) cre0 = (0,1) crN1, cr0, cr1 :: CanonRep_ -- | Canon rep for -1 crN1 = [creN1] -- | Canon rep for 0 cr0 = [cre0] -- | Canon rep for 1 cr1 = [] -- Yes, a canonical "1" is just an empty list. -- | Pattern for a negative CR_ pattern PNeg :: forall a a1. (Num a, Num a1, Eq a, Eq a1) => [(a1, a)] pattern PNeg <- ((-1,1):_) crNegative, crPositive :: CR_ -> Bool -- | Check if a CR_ is negative. crNegative PNeg = True crNegative _ = False -- | Check if a CR_ is positive. crPositive PZero = False crPositive x = not $ crNegative x -- | Canon rep validity check: -- The 2nd param checks the validity of the base, the 3rd of the exponent. -- The base pred should be some kind of prime or psuedo-prime test unless you knew for -- certain the bases are prime. There are two choices for the exp pred: -- positiveOnly (True) or nonZero (False) (which allows for "rationals"). crValid :: CR_ -> (Integer -> Bool) -> Bool -> Bool crValid POne _ _ = True crValid PZero _ _ = True crValid PZeroBad _ _ = False crValid c bp ef | crNegative c = f (tail c) 1 | otherwise = f c 1 where f POne _ = True f ((cp,ce):cs) n | cp <= n || not (expPred ef ce) || not (bp cp) = False | otherwise = f cs cp f _ _ = error "Logic error in crValid'. Issue with pattern matching?" expPred b e = if b then (e > 0) else (e /= 0) crValidIntegral, crValidRational :: CR_ -> Bool crValidIntegralViaUserFunc, crValidRationalViaUserFunc :: CR_ -> (Integer -> Bool) -> Bool -- | Checks if a CR_ represents an integral number. crValidIntegral n = crValid n isPrime True -- | Checks if a CR_ is Integral and valid per user-supplied criterion. crValidIntegralViaUserFunc n f = crValid n f True -- | Checks if a CR_ is represents a rational number (inclusive of integral numbers). crValidRational n = crValid n isPrime False -- | Checks if a CR_ is Rational and valid per user-supplied criterion. crValidRationalViaUserFunc n f = crValid n f False crFromInteger, crFromI :: Integer -> (CR_, Bool) -- | Factor the number to convert it to a canonical rep. This is of course can be extremely expensive. -- (This now also returns a flag indicating True if the number was completely factored.) crFromInteger n | n == 0 = (cr0, True) | n == 1 = (cr1, True) | n == -1 = (crN1, True) | n < 0 = (creN1:cr, ff) -- in case arithmoi excludes -1 as a factor in the future | otherwise = (cr, ff) where cr = crSort cr' -- the prime factors must be in ascending order (cr', ff) = factorize (abs n) -- ToDo: pass to fcn crSort :: CR_ -> CR_ crSort = sortBy prd where prd (p1, _) (p2, _) | p1 < p2 = LT | otherwise = GT -- Note: if crFactCutoff is <= 0, complete factorization is attempted -- and all of the cutoff / spFactor logic is not used. crFactCutoff, crTrialDivCutoff, crSmallFactCutoff, crTrialDivCutoffSq :: Integer -- | Factorization cutoff (Note: if this is <= 0, complete factorization is always attempted) crFactCutoff = (10 :: Integer) ^ (80 :: Int) crTrialDivCutoff = 100000 -- | use this as the higher cutoff if the number is beyond the factorization cutoff crSmallFactCutoff = 10000000 crTrialDivCutoffSq = crTrialDivCutoff * crTrialDivCutoff -- factorize and deftStdGenFact were adapted from arithmoi factorize :: Integer -> ([(Integer,Integer)], Bool) factorize n = (map (\(p,e) -> (p, toInteger e)) f, b) where (f, b) = if crFactCutoff > 0 then defStdGenFact (mkStdGen $ fromInteger n `xor` 0xdeadbeef) else (factorise' n, True) -- call underlying routine from arithmoi defStdGenFact sg = let (sfs,mb) = smallFactors (if n <= crFactCutoff then crTrialDivCutoff else crSmallFactCutoff) n in (sfs ++ case mb of Nothing -> [] Just m -> if m > crFactCutoff then [(m, 1)] else stdGenFactorisation (Just crTrialDivCutoffSq) sg Nothing m, case mb of Nothing -> True Just m -> m < crFactCutoff || isPrime m -- if less, do a complete factorization ) -- | Shorthand for crFromInteger function crFromI n = crFromInteger n crToInteger, crToI :: CR_ -> Integer -- | Convert a canon rep back to an Integer. crToInteger POne = 1 crToInteger PZero = 0 crToInteger c | (head c) == creN1 = -1 * (crToInteger $ tail c) -- negative number | otherwise = product $ map (\(x,y) -> x ^ y) c -- | Alias to crToInteger. crToI = crToInteger -- | Compute the modulus between a CR_ and Integer and return an Integer. crModI :: CR_ -> Integer -> Integer crModI _ 0 = error "Divide by zero error when computing n mod 0" crModI _ 1 = 0 crModI _ (-1) = 0 crModI POne PIntPos = 1 crModI PZero _ = 0 crModI c m | cn && mn = -1 * crModI (crAbs c) am | (cn && not mn) || (mn && not cn) = (signum m) * (am - f (crAbs c) am) | otherwise = f c m where cn = crNegative c mn = m < 0 am = abs m f c' m' = mod (product $ map (\(x,y) -> if (mod x m' == 0) then 0 else (pmI x (mmt y) m')) c') m' mmt e | e >= 1 = mod e $ totient m -- optimization | otherwise = error "Negative exponents are not allowed in crModI" -- | Compute modulus with all CR_ parameters. This wraps crModI. crMod :: CR_ -> CR_ -> (CR_, Bool) crMod c m = crFromI $ crModI c (crToI m) -- | Display a Canon Element (as either p^e or p). ceShow :: CanonElement_ -> String ceShow (p,e) = show p ++ if e == 1 then "" else "^" ++ (if e < 0 then "(" ++ se ++ ")" else se) where se = show e crShow, crShowRational :: CR_ -> String -- | Display a canonical representation. crShow POne = show (1 :: Integer) crShow x | null (tail x) = ceShow $ head x | otherwise = concat $ intersperse " * " $ map ceShow x -- | Display a CR_ rationally, as a quotient of its numerator and denominator. crShowRational c | d == cr1 = crShow n | otherwise = crShow n ++ "\n/\n" ++ crShow d where (n, d) = crSplit c crNegate, crAbs, crSignum :: CR_ -> CR_ -- | Negate a CR_. crNegate PZero = cr0 crNegate x | crNegative x = tail x | otherwise = creN1 : x -- | Take the absolute value of a CR_. crAbs x | crNegative x = tail x | otherwise = x -- | Compute the signum and return as CR_. crSignum PZero = cr0; crSignum x | crNegative x = crN1 | otherwise = cr1 -- | CR_ Compare Function crCmp :: CR_ -> CR_ -> Ordering crCmp POne y = crCmp1 y True crCmp x POne = crCmp1 x False crCmp x y | x == y = EQ | xN && yN = crCmp (tail y) (tail x) | xN = LT | yN = GT | eqZero x = LT | eqZero y = GT | otherwise = case compare (crLogDouble x) (crLogDouble y) of EQ -> compare (crLog x) (crLog y) -- We have to break out the big guns, both evaluate to infinity cmp -> cmp where xN = crNegative x yN = crNegative y eqZero PZero = True; eqZero _ = False -- Internal: Compare when either term is 1. crCmp1 :: CR_ -> Bool -> Ordering crCmp1 PNeg b = if b then GT else LT crCmp1 PZero b = if b then GT else LT crCmp1 _ b = if b then LT else GT crMin, crMax :: CR_ -> CR_ -> CR_ -- | Min function crMin x y = case crCmp x y of GT -> y _ -> x -- | Max functon crMax x y = case crCmp x y of LT -> y _ -> x divisionError, divByZeroError, zeroDivZeroError, negativeLogError :: String divisionError = "For this function, the dividend must be a multiple of the divisor." divByZeroError = "Division by zero error!" zeroDivZeroError = "Zero divided by zero is undefined!" negativeLogError = "The log of a negative number is undefined!" -- | Strict division: Generate error if exact division is not possible. crDivStrict :: CR_ -> CR_ -> CR_ crDivStrict x y = case crDiv x y of Left errorMsg -> error errorMsg Right quotient -> quotient -- | Attempt to take the quotient. crDiv :: CR_ -> CR_ -> Either String CR_ crDiv PZero PZero = Left zeroDivZeroError crDiv PZero _ = Right cr0 crDiv _ PZero = Left divByZeroError crDiv x' y' = f x' y' where -- call this after handling zeroes above, then division just occurs within here f x POne = Right x f POne _ = Left divisionError f x y | crNegative y = f (crNegate x) (crAbs y) | otherwise = case compare xp yp of LT -> case f xs y of Left _ -> Left divisionError Right r -> Right ((xp, xe):r) EQ| (xe > ye) -> case f xs ys of Left _ -> Left divisionError Right r -> Right ((xp,xe-ye):r) EQ| (xe == ye) -> f xs ys _ -> Left divisionError where ((xp,xe):xs) = x ((yp,ye):ys) = y crMult, crDivRational, crGCD, crLCM :: CR_ -> CR_ -> CR_ -- | Multiply two crs by summing the exponents for each prime. crMult PZero _ = cr0 crMult _ PZero = cr0 crMult POne y = y crMult x POne = x crMult x y = case compare xp yp of LT -> xh : crMult xs y -- cancel double negs or expnts adding to zero EQ -> if crNegative x || expSum == 0 then r else (xp, expSum) : r where r = crMult xs ys GT -> yh : crMult x ys where (xh@(xp,xe):xs) = x (yh@(yp,ye):ys) = y expSum = xe + ye -- | Division of rationals is equivalent to multiplying with negated exponents. crDivRational x y = crMult (crRecip y) x -- multiply by the reciprocal -- | For the GCD (Greatest Common Divisor), take the lesser of two exponents for each prime encountered. crGCD PZero y = y crGCD x PZero = x crGCD x y | crNegative x || crNegative y = f (crAbs x) (crAbs y) | crFactCutoff > 0 && ((spIncompFact x && spUnsmooth y) || -- either has an imcomplete factorization and the other (spIncompFact y && spUnsmooth x)) = f spx spy -- in case of unfactored bits | otherwise = f x y where f POne _ = cr1 f _ POne = cr1 f x' y' = case compare xp yp of LT -> f xs y' EQ -> (xp, min xe ye) : f xs ys GT -> f x' ys where ((xp,xe):xs) = x' ((yp,ye):ys) = y' (spx, spy) = spFactor x y -- | For the LCM (Least Common Multiple), take the max of two exponents for each prime encountered. crLCM PZero _ = cr0 crLCM _ PZero = cr0 crLCM x y | crNegative x || crNegative y = f (crAbs x) (crAbs y) | crFactCutoff > 0 && ((spIncompFact x && spUnsmooth y) || -- either has an imcomplete factorization and the other (spIncompFact y && spUnsmooth x)) = f spx spy -- in case of unfactored bits | otherwise = f x y where f POne y' = y' f x' POne = x' f x' y' = case compare xp yp of LT -> xh : f xs y' EQ -> (xp, max xe ye) : f xs ys GT -> yh : f x' ys where (xh@(xp,xe):xs) = x' (yh@(yp,ye):ys) = y' (spx, spy) = spFactor x y -- special factor: This covers the case where we have unfactored large components but on comparison with another -- cr we can see that the component can be reduced. We partition the cr into -- three pieces: small factor cutoff, prime, composite (implying it's > factor. cutoff) spFactor :: CR_ -> CR_ -> (CR_, CR_) spFactor x y = (sx ++ (grp $ crSort $ px ++ spF cx (py ++ cy)), sy ++ (grp $ crSort $ py ++ spF cy (px ++ cx)) ) where spl n = (s, p, c) where (s, r) = partition spSmooth n (p, c) = partition (\ce -> not $ spBigComposite ce) r (sx, px, cx) = spl x (sy, py, cy) = spl y grp (n:ns) = g' n ns grp _ = error "The list to be grouped in spFactor must have at least one element" g' (b,e) (r:rs) = if b == b' then g' (b, e + e') rs -- group by common base on sorted list else (b,e):g' (b', e') rs where (b', e') = r g' ce _ = [ce] -- take each entry in f and compute the gcd. ToDo: replace with fold spF n (f:fs) = spF (concat $ map (proc f) n) fs -- this is quadratic but likely with very short lists where proc (pf, _) (pn, en) = if g == 1 || g == pn then [(pn, en)] else [(g, en), (div pn g, en)] where g = gcd pn pf spF n _ = n -- Predicates used for special cases of GCD and LCM spUnsmooth, spIncompFact :: CR_ -> Bool spUnsmooth = any (\ce -> not $ spSmooth ce) spIncompFact = any spBigComposite spBigComposite, spSmooth :: CanonElement_ -> Bool spSmooth (b,_) = b <= crSmallFactCutoff spBigComposite (b,_) = b > crFactCutoff && (not $ isPrime b) -- | Take the reciprocal by raising a CR to the -1 power (equivalent to multiplying exponents by -1). crRecip :: CR_ -> CR_ crRecip x = crExp x (-1) True rootError :: CR_ -> Integer -> String rootError c r = "crRoot: All exponents must be multiples of " ++ (show r) ++ ". Not so with " ++ (show c) -- | Attempt to compute a particular root of a CR_. crRoot :: CR_ -> Integer -> CR_ crRoot _ PIntNeg = error "r-th roots are not allowed when r <= 0" crRoot POne _ = cr1 crRoot c r | crNegative c = if mod r 2 == 1 then creN1 : crRoot (crAbs c) r else error "Imaginary numbers not allowed: Even root of negative number requested" | otherwise = if mod ce r == 0 then (cp, div ce r) : crRoot cs r else error $ rootError c r where ((cp,ce):cs) = c -- | Takes the maximum root of the number. Generally, the abs value would be passed to the function. crMaxRoot :: CR_ -> Integer crMaxRoot c = foldr (\x -> flip gcd $ snd x) 0 c -- | Exponentiation. Note: this does allow for negative exponentiation if bool flag is True. crExp :: CR_ -> Integer -> Bool -> CR_ crExp _ PIntNeg False = error "Per param flag, negative exponentiation is not allowed here." crExp PZero PIntNPos _ = error "0^e where e <= 0 is either undefined or illegal" crExp PZero _ _ = cr0 crExp POne _ _ = cr1 crExp _ 0 _ = cr1 crExp c em _ = ce c where ce c' | crNegative c' = if mod em 2 == 1 then creN1 : absTail else absTail | otherwise = map (\(p,e) -> (p, e * em)) c' where absTail = ce $ crAbs c' -- | This log function is much more expensive but accurate. You have an "infinity" problem potentially with crLogDouble. crLog :: CR_ -> Rational crLog PNeg = error negativeLogError crLog c = sum $ map (\(p,e) -> (toRational $ logDouble $ fromIntegral p) * (fromIntegral e)) c where logDouble :: Double -> Double logDouble n = log n -- | Returns log of CR_ as a Double. crLogDouble :: CR_ -> Double crLogDouble PNeg = error negativeLogError crLogDouble c = sum $ map (\(x,y) -> log (fromIntegral x) * fromIntegral y) c crNumer, crDenom, crRadical :: CR_ -> CR_ -- | Compute numerator (by filtering on positive exponents). crNumer c = filter (\(_,e) -> e > 0) c -- | Compute denominator. (Grab the primes with negative exponents and then flip the sign of the exponents.) crDenom c = map (\(p,e) -> (p, (-1) * e)) $ filter (\(_,e) -> e < 0) c -- | Check if a CR_ represents an integer. crIntegral :: CR_ -> Bool crIntegral x = all (\(_,e) -> e > 0) x -- all exponents must be positive -- | Split a CR_ into its Numerator and Denominator. crSplit :: CR_ -> (CR_, CR_) crSplit c = (crNumer c, crDenom c) -- | Convert a CR_ to a Rational number. crToRational :: CR_ -> Rational crToRational c = (crToI $ crNumer c) :% (crToI $ crDenom c) -- | Compute the Radical of a CR_ (http://en.wikipedia.org/wiki/Radical_of_an_integer). -- Its the product of the unique primes in its factorization. crRadical n = map (\(p,_) -> (p, 1)) n -- | The Op(eration) is intended to be plus or minus. integerApply :: (Integer -> Integer -> Integer) -> CR_ -> CR_ -> Integer integerApply op x y = op (crToI x) (crToI y) -- | Calls integerApply and returns a CR_. crSimpleApply :: (Integer -> Integer -> Integer) -> CR_ -> CR_ -> (CR_, Bool) crSimpleApply op x y = crFromI $ integerApply op x y {- No longer needed. Different criteria used now pattern PPrime :: forall a a1. (Eq a, Num a, Num a1, Ord a1) => [(a1, a)] pattern PPrime <- [(compare 1 -> LT, 1)] -- of form x^1 where x > 1 -- prime (assumption p has already been verified to be prime) -} crPrime, crHasSquare :: CR_ -> Bool -- | Check if a number is a prime. crPrime cr | length cr > 1 || null cr = False | e == 1 && b > 1 && (crFactCutoff == 0 || b <= crFactCutoff || (b > crFactCutoff && isPrime b)) = True | otherwise = False where (b,e) = head cr -- | Check if a number has a squared (or higher) factor. crHasSquare = any (\(_,e) -> e > 1) -- | Divisor functions should be called with integral CRs (no negative exponents). crNumDivisors, crTau, crTotient, crPhi :: CR_ -> Integer crNumDivisors cr = product $ map (\(_,e) -> 1 + e) cr -- does return 1 for cr1 crTau = crNumDivisors crTotient cr = product $ map (\(p,e) -> (p-1) * p^(e-1)) cr crPhi = crTotient -- | Compute the nth divisor. This is zero based. -- Note: This is deterministic but it's not ordered by the value of the divisor. crNthDivisor :: Integer -> CR_ -> CR_ crNthDivisor 0 _ = cr1 crNthDivisor _ POne = error "Bad div num requested" crNthDivisor n c | m == 0 = r | otherwise = (cb,m) : r where (cb,ce):cs = c r = crNthDivisor (div n (ce + 1)) cs -- first param is the next n m = mod n (ce + 1) -- | Consider this to be the inverse of the crNthDivisor function. crWhichDivisor :: CR_ -> CR_ -> Integer crWhichDivisor d c | crPositive d == False || crPositive c == False = error "crWhichDivisor: Both params must be positive" | otherwise = f d c where f POne _ = 0 f _ POne = error "Not a valid divisor" f d' c' | dp < cp || (dp == cp && de > ce) = error "Not a valid divisor" | dp == cp = de + (ce + 1) * (f ds cs) | otherwise = (ce + 1) * (f d cs) where ((dp, de):ds) = d' ((cp, ce):cs) = c' -- | Efficiently compute all of the divisors based on the canonical representation. crDivisors :: CR_ -> [CR_] crDivisors c = foldr1 cartProd $ map pwrDivList c where cartProd xs ys = [x ++ y | y <- ys, x <- xs] pwrDivList (n,e) = [if y == 0 then cr1 else [(n,y)] | y <- [0..(fromInteger e)]] -- | Like the crDivisors function, except that it returns pairs of the CR_ and resp. numeric value, instead of just the CR_. crDivsPlus :: CR_ -> [(CR_, Integer)] crDivsPlus c = foldr1 cartProd (map pwrDivList c) where cartProd xs ys = [(xl ++ yl, xv * yv) | (yl, yv) <- ys, (xl, xv) <- xs] pwrDivList (e,n) = map tr $ pwrList e n powers x = 1 : map (* x) (powers x) pwrList n e = [(n,y) | y <- zip [0..e] (take (e'+1) $ powers n)] where e' = fromInteger e tr (a,b) = (if fb == 0 then cr1 else [(a, fb)], sb) -- this just transforms the data structure where (fb, sb) = b -- | Check if underlying rep is simplified crSimplified :: CR_ -> Bool crSimplified POne = True crSimplified PZero = True crSimplified PN1 = True crSimplified c = crPrime c -- | Compute totient: Logic from deprecated arithmoi function used here. totient :: Integer -> Integer totient n | n < 1 = error "Totient only defined for positive numbers" | n == 1 = 1 | ff == False = error "Totient not computable. The number could not be totally factored based on the factorizaton cutoff." | otherwise = product $ map (\(p,e) -> (p-1) * p ^ (e-1)) cr where (cr, ff) = factorize n -- ToDo: pass the param to function -- | powerModInteger adapted here from deprecated arithmoi function. pmI :: Integer -> Integer -> Integer -> Integer pmI x p m | x < 1 || p < 0 || m < 1 = error "pmI (powerModInteger) requires: x >= 1 &&, p >= 0, m >= 1" | otherwise = f p 1 (mod x m) -- last is the running exp of mod initially where f q w e | w == 0 || q == 0 = w | q == 1 = mod (w*e) m | otherwise = f (div q 2) nw (mod (e*e) m) where nw | mod q 2 == 1 = mod (w*e) m | otherwise = w