{-# LANGUAGE PatternSynonyms, ViewPatterns, RankNTypes #-}
module Math.NumberTheory.Canon (
Canon, makeCanon, makeCanon', BareStatus(..), CanonValueType(..),
cShowFull, cShowFullAsCode, cShowAsCode, cShowAsCodeUnf, cShowUnf,
cMult, cDiv, cAdd, cSubtract, cExp,
cReciprocal, (>^), (<^),
cGCD, cLCM, cMod, cOdd, cEven, cTotient, cPhi,
cNegative, cPositive, cIntegral, cRational, cIrrational, cPrime, cSimplified,
cSplit, cNumerator, cDenominator,
cCanonical, cBare, cBareStatus, cValueType, cDelve,
cIsPrimeTower, cPrimeTowerLevel, cSuperLog, cSuperLogCmp,
cTetration, cPentation, cHexation, cHeptation, cOctation, cNonation,
cTetrationL, cPentationL, cHexationL, cHeptationL, cOctationL, cNonationL,
(<^>), (<<^>>), (<<<^>>>), (<<<<^>>>>), (<<<<<^>>>>>), (|<^>|),
(<^^>), (<<^^>>), (<<<^^>>>), (<<<<^^>>>>), (<<<<<^^>>>>>), (|<^^>|),
(~^~), (~<^>~), (~<<^>>~), (~<<<^>>>~), (~<<<<^>>>>~),
(~|^|~), (~|<^>|~), (~|<<^>>|~), (~|<<<^>>>|~), (~|<<<<^>>>>|~),
(~~^~~), (~~<^>~~), (~~<<^>>~~), (~~<<<^>>>~~), (~~<<<<^>>>>~~),
(~~|^|~~), (~~|<^>|~~), (~~|<<^>>|~~), (~~|<<<^>>>|~~), (~~|<<<<^>>>>|~~),
(~~~^~~~), (~~~<^>~~~), (~~~<<^>>~~~), (~~~<<<^>>>~~~), (~~~<<<<^>>>>~~~),
(~~~|^|~~~), (~~~|<^>|~~~), (~~~|<<^>>|~~~), (~~~|<<<^>>>|~~~), (~~~|<<<<^>>>>|~~~),
(~~~~^~~~~), (~~~~<^>~~~~), (~~~~<<^>>~~~~), (~~~~<<<^>>>~~~~), (~~~~<<<<^>>>>~~~~),
(~~~~|^|~~~~), (~~~~|<^>|~~~~), (~~~~|<<^>>|~~~~), (~~~~|<<<^>>>|~~~~), (~~~~|<<<<^>>>>|~~~~),
(~~~~~^~~~~~),
cAddOpLevel, cMultOpLevel, cExpOpLevel, cTetrOpLevel,
cPentOpLevel, cHexOpLevel, cHeptOpLevel, cOctOpLevel, cNonOpLevel,
cGetHyperList, cGetHyperOp, maxHyperOpDispLevel, maxHyperOpDelveLevel,
cFactorSum, cConvertToSum, cMaxExpoToExpand, cFactorHorizon,
cApplyHy, cHyperOp, cHyperExpr, cHyperExprAny, cMaxHyperOp, cMinHyperOp, cMaxHyperOpForQC,
cHyperSum, cHyperProd, cHyperExpo, cHyperSumAny,
cHyperize, cQuasiCanonize, cQuasiCanonized, cCleanup, cGetAddends, cGetFactors, cCleanupAsNumDenPair,
CanonElement, cGetBase, cGetExponent, SuperPairC,
cGetBases, cGetBasesDeep, cGetExponents, cGetElements,
cNumDivisors, cTau, cDivisors, cNthDivisor, cWhichDivisor, cRelativelyPrime, cGetFirstNDivisors,
cN1, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9,
CycloMap, getIntegerBasedCycloMap, crCycloInitMap
)
where
import Math.NumberTheory.Primes (primes, unPrime)
import Math.NumberTheory.Primes.Testing (isPrime)
import Data.List
import Data.Maybe (fromMaybe)
import GHC.Real (Ratio(..))
import Math.NumberTheory.Canon.Internals
import Math.NumberTheory.Canon.Additive
import Math.NumberTheory.Canon.AurifCyclo
import Math.NumberTheory.Canon.Simple (CanonConv(..))
data CanonValueType = IntC | NirC | IrrC deriving (Eq, Ord, Show)
type CanonElement = (Canon, Canon)
type GCR_ = [GCRE_]
type GCRE_ = (Integer, Canon)
data Canon = Bare Integer BareStatus | Can GCR_ CanonValueType | HX Canon [Canon] CanonValueType
data BareStatus = Simp | NSim deriving (Eq, Ord, Show)
makeCanon :: Integer -> Canon
makeCanon n = fst $ makeCanon' n
makeCanon' :: Integer -> (Canon, Bool)
makeCanon' n = (f cr, ff)
where f POne = Bare 1 Simp
f c | null cs && eh == 1
= if superLogI bh > superLogICutoff
then error "Lib limitation: Can't handle massive bare numbers > cutoff"
else Bare bh (if ff then Simp else NSim)
| otherwise = Can g (gcrCVT g)
where (bh,eh):cs = c
g = map (\(p,e) -> (p, makeCanon e)) c
(cr, ff) = crFromI n
crToC :: CR_ -> Bool -> Canon
crToC POne _ = Bare 1 Simp
crToC c _ | crSimplified c = Bare (fst $ head c) Simp
| otherwise = Can g (gcrCVT g)
where g = map (\(p,e) -> (p, makeCanon e)) c
instance Eq Canon where
x == y = cEq x y
maxHyperOpDispLevel :: Integer
maxHyperOpDispLevel = 50;
maxHyperOpDelveLevel :: Canon
maxHyperOpDelveLevel = makeCanon 100;
smallCanons :: [Canon]
smallCanons = map (\n -> makeCanon n) [0..maxHyperOpDispLevel]
cAddOpLevel, cMultOpLevel, cExpOpLevel, cTetrOpLevel,
cPentOpLevel, cHexOpLevel, cHeptOpLevel, cOctOpLevel, cNonOpLevel :: Canon
(_: cAddOpLevel : cMultOpLevel : cExpOpLevel : cTetrOpLevel :
cPentOpLevel : cHexOpLevel : cHeptOpLevel : cOctOpLevel : cNonOpLevel : _) = smallCanons
cShowFull, cShowFullAsCode, cShowAsCode, cShowAsCodeUnf, cShowUnf, cShowForEqChk :: Canon -> String
cShowFull = cShow True False False False
cShowFullAsCode = cShow True True False False
cShowAsCode = cShow False True False False
cShowAsCodeUnf = cShow False True True False
cShowUnf = cShow False False True False
cShowForEqChk = cShow False False False True
instance Show Canon where
show = cShow False False False False
cShow :: Bool -> Bool -> Bool -> Bool -> Canon -> String
cShow b _ i _ (Bare n NSim)
= showI b n False i
cShow b _ i _ (Bare n Simp)
= showI b n True i
cShow b p i m (HX h l _)
| p && (cHyperExprAny h || h > maxSmallC)
= "cApplyHy " ++ showH h ++ " [" ++ (concat $ intersperse ", " $ map (cShow b p i m) cl) ++ "] True"
| otherwise = fmt1 (head cl) ++ s' (tail cl)
where cl | h == cAddOpLevel && any cNegative l = pR ++ nR
| otherwise = l
where (pR, nR) = partition cPositive l
fmt1 hD | not (cHyperExpr hD) && h == cMultOpLevel = cShow b p i m hD
| otherwise = showH hD
showH c | h == cAddOpLevel || cBare c || (i && canConvToI c) = rep
| (not p) && cHyperExpr c = "{" ++ rep ++ "}"
| otherwise = "(" ++ rep ++ ")"
where rep = cShow b p i m c
fmtHy f | f = "-"
| cHyperExprAny h || h > maxSmallC = fmt' h
| p && h == cExpOpLevel = "<^"
| otherwise = hyperOpStrings !! (fromInteger $ cToI h)
where fmt' c | (not p) && cHyperExpr c = "<H{" ++ rep' ++ "}>"
| otherwise = "<H(" ++ rep ++ ")>"
where rep = cShow b p i m c
rep' | not m = rep
| cGetHyperOp c == cAddOpLevel = cShow b p i m (fst $ cConvertToSum c)
| cGetHyperOp c == cMultOpLevel = cShow b p i m c
| otherwise = rep
s' (x:xs) = " " ++ fmtHy f' ++ " " ++ showH (if f' then (negate x) else x) ++ s' xs
where f' = h == cAddOpLevel && cNegative x
s' _ = ""
maxSmallC = smallCanons !! (fromInteger $ maxHyperOpDispLevel)
cShow b p i m c
| denom == c1 = s numer False
| otherwise = s numer True ++ " / " ++ s denom True
where (numer, denom) = cSplit c
s (Bare n f) _ = cShow b p i m (Bare n f)
s v w | i && canConvToI v = show $ cToI v
| w = "(" ++ catList ++ ")"
| otherwise = catList
where catList = concat $ intersperse " * " $ map sE $ gcr' $ cToGCR v
gcr' g@(x:y:gs)| fst x == -1 && snd y == c1 = ((fst x * fst y, snd y) : gs)
| otherwise = g
gcr' g = g
sE (p', e) | ptLevel > 2 = sp ++ " <^> " ++ s ptLevel True
| otherwise = case e of
Bare 1 _ -> sp
Bare _ _ -> sp ++ expOp ++ se
_ -> sp ++ " " ++ expOp ++ " (" ++ se ++ ")"
where ptLevel = cPrimeTowerLevelI e p' 1
sp = showI b p' (isPrime p' || p' == -1) i
se = cShow b p i m e
expOp = if p then "<^" else "^"
canConvToI :: Canon -> Bool
canConvToI c = not $ cSuperLogGT (fst $ cSuperLog c) cSuperLogCutoff
showI :: Bool -> Integer -> Bool -> Bool -> String
showI b n pOrC i | i = show n
| not pOrC = "[" ++ txt ++ "]"
| truncFlag = "(" ++ txt ++ ")"
| otherwise = txt
where txt = if truncFlag
then (show $ nd n) ++ "-digit " ++ (if pOrC then "prime" else "composite")
else show n
truncFlag = (not b) && n > integerShowCutoff
nd :: Integer -> Integer
nd n' = nd' n' 1
where nd' n'' ct = if (n'' >= 10) then nd' (div n'' 10) (ct + 1)
else ct
instance Enum Canon where
toEnum n = makeCanon $ fromIntegral n
fromEnum c = fromIntegral $ cToI c
instance Ord Canon where
compare x y = cCmp x y
instance Real Canon where
toRational c | cIrrational c = toRational $ cToD c
| otherwise = (cToI $ cNumerator c) :% (cToI $ cDenominator c)
instance Integral Canon where
toInteger c | cIntegral c = cToI c
| otherwise = floor $ cToD c
quotRem n m = fst $ cQuotRem n m crCycloInitMap
mod n m = cMod n m
instance Fractional Canon where
fromRational (n :% d) = makeCanon n / makeCanon d
(/) x y = fst $ cDiv x y crCycloInitMap
instance Num Canon where
fromInteger n = makeCanon n
x + y = fst $ cAdd x y crCycloInitMap
x - y = fst $ cSubtract x y crCycloInitMap
x * y = fst $ cMult x y crCycloInitMap
negate x = cNegate x
abs x = cAbs x
signum x = cSignum x
cCanonical :: Canon -> Bool
cCanonical (Can _ _) = True
cCanonical _ = False
cBare :: Canon -> Bool
cBare (Bare _ _) = True
cBare _ = False
cBareStatus :: Canon -> BareStatus
cBareStatus (Bare _ b) = b
cBareStatus _ = error "cBareStatus: Can only checked for 'Bare' Canons"
cValueType :: Canon -> CanonValueType
cValueType (Bare _ _) = IntC
cValueType (Can _ v) = v
cValueType (HX _ _ v) = v
cSplit :: Canon -> (Canon, Canon)
cSplit c = (cNumerator c, cDenominator c)
cEq :: Canon -> Canon -> Bool
cEq (Bare x _ ) (Bare y _ ) = x == y
cEq (Bare _ Simp) (Can _ _ ) = False
cEq (Can _ _ ) (Bare _ Simp) = False
cEq a@(HX _ _ _) b@(HX _ _ _) | signum a /= signum b = False
| h1 /= h2 = False
| h1 < cExpOpLevel = cmpHyLists cL1 cL2
| otherwise = cShowForEqChk a' == cShowForEqChk b'
where (cL1:cL2:_) = map cGetHyperList [a',b']
cmpHyLists x y | length x /= length y = False
| otherwise = c' x y []
c' l@(x:xs) (y:ys) bN | x == y = c' xs (bN++ys) []
| otherwise = c' l ys (bN ++ [y])
c' (_:_) _ _ = False
c' _ y _ = null y
(a', b') = (fst $ cConvertToSum a, fst $ cConvertToSum b)
(h1, h2) = (cGetHyperOp a', cGetHyperOp b')
cEq hx@(HX _ _ _) b | cBare b || cMaxHyperOp hx >= cPentOpLevel
= False
| otherwise = cValueType hx == cValueType b && cGetBases hx == cGetBases b
&& (cSuperLogCmp (fst $ cSuperLog hx) (fst $ cSuperLog b) == EQ)
cEq a hx@(HX _ _ _) = cEq hx a
cEq (Bare x NSim) y | cValueType y /= IntC = False
| otherwise = x == cToI y
cEq x y@(Bare _ NSim) = cEq y x
cEq (Can x a ) (Can y b) = if a /= b then False else gcrEqCheck x y
cOdd, cEven :: Canon -> Bool
cOdd = cMod2Check 1 gcrOdd
cEven = cMod2Check 0 gcrEven
cMod2Check :: Int -> (GCR_ -> Bool) -> Canon -> Bool
cMod2Check m _ (Bare x _) = mod x 2 == toInteger m
cMod2Check _ f (Can c IntC) = f c
cMod2Check m _ (HX PoA cL IntC) = mod (sum $ map (\c -> mod c c2) cL) 2 == smallCanons !! m
cMod2Check m _ (HX PoM cL IntC) = mod (product $ map (\c -> mod c c2) cL) 2 == smallCanons !! m
cMod2Check m _ (HX _ cL IntC) = mod (head cL) 2 == smallCanons !! m
cMod2Check _ _ _ = False
cGCD :: Canon -> Canon -> Canon
cGCD x y | cHyperExprAny x || cHyperExprAny y = head $ cMultiplicative x y Gcd
| otherwise = cLGApply gcrGCD x y
cLCM :: Canon -> Canon -> Canon
cLCM x y | cHyperExprAny x || cHyperExprAny y = head $ cMultiplicative x y Lcm
| otherwise = cLGApply gcrLCM x y
cCmp, cCmpH, cCmp' :: Canon -> Canon -> Ordering
cCmp (Bare x _) (Bare y _) = compare x y
cCmp x@(Can _ _) y@(Bare _ _) = gcrCmp (cToGCR x) (cToGCR y)
cCmp x@(Bare _ _) y@(Can _ _) = gcrCmp (cToGCR x) (cToGCR y)
cCmp x@(Can _ _) y@(Can _ _) = gcrCmp (cToGCR x) (cToGCR y)
cCmp x@(HX _ _ _) (Bare _ _) = if signum x == c1 then GT else LT
cCmp (Bare _ _) y@(HX _ _ _) = if signum y == c1 then LT else GT
cCmp x y | signum y == c1 && signum x /= c1 = LT
| signum x == c1 && signum y /= c1 = GT
| signum x == cN1 && signum y == cN1 = cCmp (abs y) (abs x)
| otherwise = cCmpH x y
cCmpH x@(Can _ _) y@(HX _ _ _) | not (cSuperLogGT (fst $ cSuperLog x) cSuperLogCutoff) = LT
| otherwise = cCmpH (cConvertToHyperExpr x) y
cCmpH x@(HX _ _ _) y@(Can _ _) | not (cSuperLogGT (fst $ cSuperLog y) cSuperLogCutoff) = GT
| otherwise = cCmpH x (cConvertToHyperExpr y)
cCmpH a@(HX h1 cL1 _) b@(HX h2 cL2 _)
| a == b = EQ
| (h1 == cAddOpLevel || h2 == cAddOpLevel) && aS /= a
= cCmp aS bS
| (h1 == cMultOpLevel || h2 == cMultOpLevel) && aR /= a
= cCmp aR bR
| mOa > mOb + 1 = GT
| mOb > mOa + 1 = LT
| mOa > cHexOpLevel && mOb > cHexOpLevel && candPred a && candPred b
= compare (tryLiftTail a) (tryLiftTail b)
| flag1Less = cba
| flag1More = cab
| mP && exprDomination a b = GT
| mP && exprDomination b a = LT
| bP && lA' > lB' = GT
| bP && lA' < lB' = LT
| h1 > cMultOpLevel && h1 == h2 && dominates cL1 cL2 True
= GT
| h2 > cMultOpLevel && h1 > h2 && dominates cL1 cL2 False
= GT
| h1 > cMultOpLevel && h2 == h1 && dominates cL2 cL1 True
= LT
| h1 > cMultOpLevel && h2 > h1 && dominates cL2 cL1 False
= LT
| bP = case compare (last cL1) (last cL2) of
EQ -> compare (reduce a) (reduce b)
cmp -> cmp
| otherwise = cCmp' a b
where (mOa, mOb, bP) = (cMaxHyperOp a, cMaxHyperOp b, h1 == h2 && h1 > cPentOpLevel)
mP = mOa >= hyperOpCutoff && mOb >= hyperOpCutoff
reduce c | len == 0 = error "Logic error in comparison: rFmt must have a hyper list with at least one entry"
| len == 1 = head l
| otherwise = simpleHX (cGetHyperOp c) (init l)
where (l, len) = (cGetHyperList c, length l)
(flag1Less, cba) = (h1 >= cPentOpLevel && h2 == h1 + 1, comp1Diff b a False)
(flag1More, cab) = (h2 >= cPentOpLevel && h1 == h2 + 1, comp1Diff a b True)
(hLa, hLb, lHa, lHb) = (cGetHyperList a, cGetHyperList b, length hLa, length hLb)
(lA', lB') | lHa > lHb = (cApplyHy h1 (drop lD hLa) True, last hLb)
| lHa < lHb = (last hLb, cApplyHy h2 (drop lD hLb) True)
| otherwise = (last hLa, last hLb)
where lD = abs (lHa - lHb)
((aS, bS), (_,aR,bR)) = (reduceSums a b, simpleReduce a b False)
cCmpH x y = error $ errorStrg
where errorStrg = "Logic error in cCmpH in program flow: " ++ show x ++ " vs. " ++ show y ++ "."
reduceSums :: Canon -> Canon -> (Canon, Canon)
reduceSums a b = (sum aS', negate $ sum bS')
where (aS', bS') = partition cPositive (cGetAddends diff)
diff = combineSum $ simpleHX cAddOpLevel (cGetAddends a ++ (map cNegate $ cGetAddends b))
reduceProds :: Canon -> Canon -> (Canon, Canon)
reduceProds a b = (aR', bR')
where (_:aR':bR':_) = cMultiplicative a b Gcd
dominates :: [Canon] -> [Canon] -> Bool -> Bool
dominates a' b' gtf = d' a' b' (0 :: Integer)
where d' (x:xs) (y:ys) pc | x < y = False
| otherwise = d' xs ys (pc + if x > y then 1 else 0)
d' _ (_:_) _ = False
d' (_:_) _ _ = True
d' _ _ pc = gtf || pc > 0
comp1Diff :: Canon -> Canon -> Bool -> Ordering
comp1Diff a' b' cF = if cF then r else flp r
where hLA@(aB:aE:_) = cGetHyperList a'
(lA2, lB2) = (length hLA, makeCanon $ toInteger $ length $ cGetHyperList b')
flp r' = case r' of
GT -> LT
LT -> GT
c -> c
r | lA2 < ml6 = LT
| lA2 > 2 = GT
| aE > lB2 = GT
| aE < lB2 = LT
| otherwise = compare (simpleHX (cGetHyperOp b') (replicate (fromInteger $ cToI aE) aB)) b'
ml6 = maxHypLen cHexOpLevel b'
maxHypLen :: Canon -> Canon -> Int
maxHypLen h c = mhl c 0
where mhl c' mx | cHyperExprAny c' = if cGetHyperOp c' == h
then max mx (length cL)
else (foldl1 max $ map (maxHypLen h) cL)
| otherwise = 0
where cL = cGetHyperList c'
hyperOpCutoff :: Canon
hyperOpCutoff = cTetrOpLevel
exprDomination :: Canon -> Canon -> Bool
exprDomination d s = eD d s False
eD :: Canon -> Canon -> Bool -> Bool
eD d' s' b' | notBoth s' d' && not b' = s' < d'
| notBoth s' d' && b' = s' <= d'
| s' == d' = b'
| s' /= sRp = eD dRp sRp b'
| rC = rC
| b' && (compare d' s' /= LT) = True
| otherwise = False
where notBoth x y = not (cMaxHyperOp x >= hyperOpCutoff && cMaxHyperOp y >= hyperOpCutoff)
(sRs, dRs) = if b' && (cHyperSum s' || cHyperSum d') then reduceSums s' d' else (s', d')
(sRp, dRp) = if b' && (cHyperProd sRs || cHyperProd dRs) then reduceProds sRs dRs else (sRs, dRs)
rC = any (\e -> eD e s' False || eD e s' True) $ cGetHyperList d'
cCmp' a b | aH == cPentOpLevel && bH == cTetrOpLevel && any cHyperExprAny (cGetHyperList b) && cSuperLogGT slb sla
= LT
| bH == cPentOpLevel && aH == cTetrOpLevel && any cHyperExprAny (cGetHyperList a) && cSuperLogGT sla slb
= GT
| aH >= cPentOpLevel && aH > bH
= GT
| bH >= cPentOpLevel && bH > aH
= LT
| aH <= cTetrOpLevel && bH <= cTetrOpLevel && cSuperLogGT sla slb
= GT
| aH <= cTetrOpLevel && bH <= cTetrOpLevel && cSuperLogGT slb sla
= LT
| bBh == cPentOpLevel && aBh == bBh
= pCmp a b
| aBh == cAddOpLevel || bBh == cAddOpLevel || cmpAddends /= EQ
= cmpAddends
| aBh == cMultOpLevel || bBh == cMultOpLevel || cmpFactors /= EQ
= cmpFactors
| aBh == bBh && aBh > cMultOpLevel && cmpHyperList /= EQ
= cmpHyperList
| cSuperLogGT sla slb = GT
| cSuperLogGT slb sla = LT
| otherwise = error $ "Unable to accurately compare a = " ++ show a ++ " and b = " ++ show b
where (aH, bH) = (cMaxHyperOp a, cMaxHyperOp b)
(aBh, bBh) = (cGetHyperOp a, cGetHyperOp b)
(sla, slb) = (fst $ cSuperLog a, fst $ cSuperLog b)
cmpList f = compare (sort $ f a) (sort $ f b)
cmpAddends = cmpList cGetAddends
cmpFactors = cmpList cGetFactors
cmpHyperList = cmpList cGetHyperList
pCmp :: Canon -> Canon -> Ordering
pCmp a b | pA > pB = GT
| pA < pB = LT
| otherwise = cSuperLogCmp sla' slb'
where pTail x = cApplyHy aBh (tail $ cGetHyperList x) False
(pA, pB) = (pTail a, pTail b)
sl x t = fst $ cSuperLog $ simpleHX aBh (x:[t - m + 2])
sla' = sl (head $ cGetHyperList a) pA
slb' = sl (head $ cGetHyperList b) pB
m = min pA pB
aBh = cGetHyperOp a
cApplyHy :: Canon -> [Canon] -> Bool -> Canon
cApplyHy ho a b = if length a == 0 && b
then error "cApplyHy: Null list passed. Specified as fatal condition by calling fcn"
else fst (cHyperOp ho a crCycloInitMap)
cMaxHyperOp :: Canon -> Canon
cMaxHyperOp = findSigHyOp max
cMinHyperOp :: Canon -> Canon
cMinHyperOp = findSigHyOp mHo
where mHo a b | a == b = a
| a == c0 = b
| b == c0 = a
| otherwise = min a b
findSigHyOp :: (Canon -> Canon -> Canon) -> Canon -> Canon
findSigHyOp _ (Bare _ _) = c0
findSigHyOp f (Can g _) = foldl1 f $ map runningSig g
where runningSig (_, e) | e == c1 = cMultOpLevel
| otherwise = f cExpOpLevel (findSigHyOp f e)
findSigHyOp f (HX h hl _) = f h (foldl1 f $ map (findSigHyOp f) hl)
cMaxHyperOpForQC :: Canon -> Canon
cMaxHyperOpForQC c | cHyperSum c = c1
| cHyperProd c = foldr max (cGetHyperOp c) $ map cMaxHyperOpForQC $ cGetFactors c
| cHyperExpr c = max (cGetHyperOp c) (cMaxHyperOpForQC $ head $ cGetHyperList c)
| otherwise = c0
cQuotRem :: Canon -> Canon -> CycloMap -> ((Canon, Canon), CycloMap)
cQuotRem x y m | cHyperExprAny x || cHyperExprAny y = ((hQ, c0), mR)
| cIntegral x && cIntegral y = ((gcrToC q', md'), m'')
| otherwise = error "cQuotRem: Must both parameters must be integral."
where (q', md', m'') = case gcrDiv (cToGCR x) gy of
Left _ -> (q, md, m')
Right quotient -> (quotient, c0, m)
where gy = cToGCR y
md = cMod x y
q = gcrDivStrict (cToGCR d) gy
(d, m') = cSubtract x md m
(hQ, mR) = cDiv x y m
cMod :: Canon -> Canon -> Canon
cMod c m | not (cIntegral c) || not (cIntegral m)
= error "cMod: Must both parameters must be integral"
| c < m = c
| m == cGCD c m = c0
| otherwise = makeCanon $ cModI c (cToI m)
cModI :: Canon -> Integer -> Integer
cModI _ 0 = error "cModI: Divide by zero error when computing n mod 0"
cModI _ 1 = 0
cModI _ (-1) = 0
cModI Pc1 PIntPos = 1
cModI Pc0 _ = 0
cModI c m | cn && mn = -1 * cma
| (cn && not mn) ||
(mn && not cn) = signum m * ((abs m) - cma)
| otherwise = cModI' c m
where (cn, mn, cma) = (cNegative c, m < 0, cModI' (abs c) (abs m))
cModI' (Bare n _) m' = mod n m'
cModI' (Can c' _) m' = if c == makeCanon m' then 0 else mod (product $ map pM c') m'
where pM (b,e) = if (mod b m' == 0) then 0 else (pmI b (mmt e) m')
mmt e = cModI e (totient m')
cModI' (HX PoA cL _) m' = mod (sum $ map (\ce -> cModI ce m') cL) m'
cModI' h@(HX PoM cL _) m' = if (cModI (product $ cGetBases h) m' == 0)
then 0
else mod (product $ map (\ce -> cModI ce m') cL) m'
cModI' (HX PoE cL _) m' = cModI (foldr1 (<^) (b':tail cL)) m'
where b' = makeCanon $ cModI (head cL) m'
cModI' (HX h cL _) m' | h == cTetrOpLevel && length cL == 2 &&
not (cHyperExprAny (cL !! 1)) && totient twrHeight > m'
= cModHyTwr (head cL) m' twrHeight
| otherwise = cModHyper (head cL) m'
where twrHeight = cToI $ cL !! 1
cModHyper b m' | (all (\e -> elem e bB) $ cGetBases $ mC') = 0
| otherwise = cToI $ f mC'
where (bB, mC') = (cGetBases b, makeCanon m')
f mC | mC == c2 = if cEven b then c0 else c1
| otherwise = cMod (b <^ f phi) mC
where phi = fst $ cPhi mC crCycloInitMap
cModHyTwr b m' s | r == 0 = 0
| otherwise = cToI $ cm' (s-1) r
where r = makeCanon $ cModI b m'
cm' y lv | y == 0 = nv
| otherwise = cm' (y-1) nv
where nv = makeCanon $ cModI (r <^ lv) m'
cTotient, cPhi :: Canon -> CycloMap -> (Canon, CycloMap)
cTotient c m | (not $ cIntegral c) || cNegative c = error "Not defined for non-integral or negative numbers"
| not $ cSimplified c = error "cTotient Can't compute if number not completely factored"
| c == c0 = (c0, m)
| otherwise = f (cToGCR c) c1 m
where f [] prd m' = (prd, m')
f ((p,e):gs) prd m' = f gs wp mw
where cp = makeCanon p
(pM1, mp) = cSubtract cp c1 m'
(eM1, me) = cSubtract e c1 mp
(pxeM1, mpm) = cExp cp eM1 False me
(nprd, mprd) = cMult pM1 pxeM1 mpm
(wp, mw) = cMult prd nprd mprd
cPhi = cTotient
hyperOpStrings :: [String]
hyperOpStrings = [
"", "+", "*", "^", "<^>", "<<^>>", "<<<^>>>", "<<<<^>>>>", "<<<<<^>>>>>", "|<^>|",
"~^~", "~<^>~", "~<<^>>~", "~<<<^>>>~", "~<<<<^>>>>~",
"~|^|~", "~|<^>|~", "~|<<^>>|~", "~|<<<^>>>|~", "~|<<<<^>>>>|~",
"~~^~~", "~~<^>~~", "~~<<^>>~~", "~~<<<^>>>~~", "~~<<<<^>>>>~~",
"~~|^|~~", "~~|<^>|~~", "~~|<<^>>|~~", "~~|<<<^>>>|~~", "~~|<<<<^>>>>|~~",
"~~~^~~~", "~~~<^>~~~", "~~~<<^>>~~~", "~~~<<<^>>>~~~", "~~~<<<<^>>>>~~~",
"~~~|^|~~~", "~~~|<^>|~~~", "~~~|<<^>>|~~~", "~~~|<<<^>>>|~~~", "~~~|<<<<^>>>>|~~~",
"~~~~^~~~~", "~~~~<^>~~~~", "~~~~<<^>>~~~~", "~~~~<<<^>>>~~~~", "~~~~<<<<^>>>>~~~~",
"~~~~|^|~~~~", "~~~~|<^>|~~~~", "~~~~|<<^>>|~~~~", "~~~~|<<<^>>>|~~~~", "~~~~|<<<<^>>>>|~~~~",
"~~~~~^~~~~~"]
infixr <^>, <<^>>, <<<^>>>, <<<<^>>>>, <<<<<^>>>>>, |<^>|,
~^~, ~<^>~, ~<<^>>~, ~<<<^>>>~, ~<<<<^>>>>~,
~|^|~, ~|<^>|~, ~|<<^>>|~, ~|<<<^>>>|~, ~|<<<<^>>>>|~,
~~^~~, ~~<^>~~, ~~<<^>>~~, ~~<<<^>>>~~, ~~<<<<^>>>>~~,
~~|^|~~, ~~|<^>|~~, ~~|<<^>>|~~, ~~|<<<^>>>|~~, ~~|<<<<^>>>>|~~,
~~~^~~~, ~~~<^>~~~, ~~~<<^>>~~~, ~~~<<<^>>>~~~, ~~~<<<<^>>>>~~~,
~~~|^|~~~, ~~~|<^>|~~~, ~~~|<<^>>|~~~, ~~~|<<<^>>>|~~~, ~~~|<<<<^>>>>|~~~,
~~~~^~~~~, ~~~~<^>~~~~, ~~~~<<^>>~~~~, ~~~~<<<^>>>~~~~, ~~~~<<<<^>>>>~~~~,
~~~~|^|~~~~, ~~~~|<^>|~~~~, ~~~~|<<^>>|~~~~, ~~~~|<<<^>>>|~~~~, ~~~~|<<<<^>>>>|~~~~,
~~~~~^~~~~~
(<^>), (<<^>>), (<<<^>>>), (<<<<^>>>>), (<<<<<^>>>>>), (|<^>|),
(~^~), (~<^>~), (~<<^>>~), (~<<<^>>>~), (~<<<<^>>>>~),
(~|^|~), (~|<^>|~), (~|<<^>>|~), (~|<<<^>>>|~), (~|<<<<^>>>>|~),
(~~^~~), (~~<^>~~), (~~<<^>>~~), (~~<<<^>>>~~), (~~<<<<^>>>>~~),
(~~|^|~~), (~~|<^>|~~), (~~|<<^>>|~~), (~~|<<<^>>>|~~), (~~|<<<<^>>>>|~~),
(~~~^~~~), (~~~<^>~~~), (~~~<<^>>~~~), (~~~<<<^>>>~~~), (~~~<<<<^>>>>~~~),
(~~~|^|~~~), (~~~|<^>|~~~), (~~~|<<^>>|~~~), (~~~|<<<^>>>|~~~), (~~~|<<<<^>>>>|~~~),
(~~~~^~~~~), (~~~~<^>~~~~), (~~~~<<^>>~~~~), (~~~~<<<^>>>~~~~), (~~~~<<<<^>>>>~~~~),
(~~~~|^|~~~~), (~~~~|<^>|~~~~), (~~~~|<<^>>|~~~~), (~~~~|<<<^>>>|~~~~), (~~~~|<<<<^>>>>|~~~~),
(~~~~~^~~~~~)
:: Canon -> Canon -> Canon
a <^> b = cTetration a b
a <<^>> b = cPentation a b
a <<<^>>> b = cHexation a b
a <<<<^>>>> b = cHeptation a b
a <<<<<^>>>>> b = cOctation a b
a |<^>| b = cNonation a b
a ~^~ b = cApplyHy (makeCanon 10) [a,b] True
a ~<^>~ b = cApplyHy (makeCanon 11) [a,b] True
a ~<<^>>~ b = cApplyHy (makeCanon 12) [a,b] True
a ~<<<^>>>~ b = cApplyHy (makeCanon 13) [a,b] True
a ~<<<<^>>>>~ b = cApplyHy (makeCanon 14) [a,b] True
a ~|^|~ b = cApplyHy (makeCanon 15) [a,b] True
a ~|<^>|~ b = cApplyHy (makeCanon 16) [a,b] True
a ~|<<^>>|~ b = cApplyHy (makeCanon 17) [a,b] True
a ~|<<<^>>>|~ b = cApplyHy (makeCanon 18) [a,b] True
a ~|<<<<^>>>>|~ b = cApplyHy (makeCanon 19) [a,b] True
a ~~^~~ b = cApplyHy (makeCanon 20) [a,b] True
a ~~<^>~~ b = cApplyHy (makeCanon 21) [a,b] True
a ~~<<^>>~~ b = cApplyHy (makeCanon 22) [a,b] True
a ~~<<<^>>>~~ b = cApplyHy (makeCanon 23) [a,b] True
a ~~<<<<^>>>>~~ b = cApplyHy (makeCanon 24) [a,b] True
a ~~|^|~~ b = cApplyHy (makeCanon 25) [a,b] True
a ~~|<^>|~~ b = cApplyHy (makeCanon 26) [a,b] True
a ~~|<<^>>|~~ b = cApplyHy (makeCanon 27) [a,b] True
a ~~|<<<^>>>|~~ b = cApplyHy (makeCanon 28) [a,b] True
a ~~|<<<<^>>>>|~~ b = cApplyHy (makeCanon 29) [a,b] True
a ~~~^~~~ b = cApplyHy (makeCanon 30) [a,b] True
a ~~~<^>~~~ b = cApplyHy (makeCanon 31) [a,b] True
a ~~~<<^>>~~~ b = cApplyHy (makeCanon 32) [a,b] True
a ~~~<<<^>>>~~~ b = cApplyHy (makeCanon 33) [a,b] True
a ~~~<<<<^>>>>~~~ b = cApplyHy (makeCanon 34) [a,b] True
a ~~~|^|~~~ b = cApplyHy (makeCanon 35) [a,b] True
a ~~~|<^>|~~~ b = cApplyHy (makeCanon 36) [a,b] True
a ~~~|<<^>>|~~~ b = cApplyHy (makeCanon 37) [a,b] True
a ~~~|<<<^>>>|~~~ b = cApplyHy (makeCanon 38) [a,b] True
a ~~~|<<<<^>>>>|~~~ b = cApplyHy (makeCanon 39) [a,b] True
a ~~~~^~~~~ b = cApplyHy (makeCanon 40) [a,b] True
a ~~~~<^>~~~~ b = cApplyHy (makeCanon 41) [a,b] True
a ~~~~<<^>>~~~~ b = cApplyHy (makeCanon 42) [a,b] True
a ~~~~<<<^>>>~~~~ b = cApplyHy (makeCanon 43) [a,b] True
a ~~~~<<<<^>>>>~~~~ b = cApplyHy (makeCanon 44) [a,b] True
a ~~~~|^|~~~~ b = cApplyHy (makeCanon 45) [a,b] True
a ~~~~|<^>|~~~~ b = cApplyHy (makeCanon 46) [a,b] True
a ~~~~|<<^>>|~~~~ b = cApplyHy (makeCanon 47) [a,b] True
a ~~~~|<<<^>>>|~~~~ b = cApplyHy (makeCanon 48) [a,b] True
a ~~~~|<<<<^>>>>|~~~~ b = cApplyHy (makeCanon 49) [a,b] True
a ~~~~~^~~~~~ b = cApplyHy (makeCanon 50) [a,b] True
cTetration, cPentation, cHexation, cHeptation, cOctation, cNonation :: Canon -> Canon -> Canon
cTetration a b = cApplyHy cTetrOpLevel [a,b] True
cPentation a b = cApplyHy cPentOpLevel [a,b] True
cHexation a b = cApplyHy cHexOpLevel [a,b] True
cHeptation a b = cApplyHy cHeptOpLevel [a,b] True
cOctation a b = cApplyHy cOctOpLevel [a,b] True
cNonation a b = cApplyHy cNonOpLevel [a,b] True
infixr 9 <^^>, <<^^>>, <<<^^>>>, <<<<^^>>>>, <<<<<^^>>>>>, |<^^>|
(<^^>), (<<^^>>), (<<<^^>>>), (<<<<^^>>>>), (<<<<<^^>>>>>), (|<^^>|) :: Canon -> [Canon] -> Canon
a <^^> b = fst $ cTetrationL a b crCycloInitMap
a <<^^>> b = fst $ cPentationL a b crCycloInitMap
a <<<^^>>> b = fst $ cHexationL a b crCycloInitMap
a <<<<^^>>>> b = fst $ cHeptationL a b crCycloInitMap
a <<<<<^^>>>>> b = fst $ cOctationL a b crCycloInitMap
a |<^^>| b = fst $ cNonationL a b crCycloInitMap
cTetrationL, cPentationL, cHexationL, cHeptationL, cOctationL, cNonationL
:: Canon -> [Canon] -> CycloMap -> (Canon, CycloMap)
cTetrationL a b m = cHyperOp cTetrOpLevel (a:b) m
cPentationL a b m = cHyperOp cPentOpLevel (a:b) m
cHexationL a b m = cHyperOp cHexOpLevel (a:b) m
cHeptationL a b m = cHyperOp cHeptOpLevel (a:b) m
cOctationL a b m = cHyperOp cOctOpLevel (a:b) m
cNonationL a b m = cHyperOp cNonOpLevel (a:b) m
cHyperOp :: Canon -> [Canon] -> CycloMap -> (Canon, CycloMap)
cHyperOp n l@(a:b:cs) m
| any (not . cIntegral) (n:l) = error "cHyperOp requires the 1st 2 parameters to be integral at this time."
| b < cN1 && n > cExpOpLevel = error $ hyperLvlError b n
| n > c2 && any cNegative (b:cs) = error "cHyperOp: At this time, all trailing entries must be >= 0 when using exponentiation or greater."
| cNegative a && n > c3 = error "cHyperOp: At this time, the base must be >= 0 when using tetration or greater."
| cNegative a && n == c3 = (if oddPwr then negate absHe else absHe, m)
| n < c0 = error "cHyperOp: Requires the level n >= 0"
| any (== c0) l = if n == cAddOpLevel then filterV c0
else (if n == cMultOpLevel then (c0, m) else stripVs c0)
| any (== c1) l && n > cAddOpLevel
= if n == cMultOpLevel then filterV c1 else stripVs c1
| (a /= c0 && a /= c1 && b > c1 && not (a == c2 && b == c2)) ||
n == c1 || n == c2 = tryToDemoteOrPromote
| null cs' = cHyperOpSpecial (toInteger n) a b m
| otherwise = error "Can not handle special cases with more than 2 params at this time"
where
absHe = fst $ cHyperOp n ((abs a):b:cs) m
oddPwr = cOdd $ fst $ cHyperOp n (b:cs) m
tryToDemoteOrPromote
| n == cAddOpLevel = (sum l, m)
| n == cMultOpLevel = (product l, m)
| hyperFree && n == cExpOpLevel = (foldr1 (<^) l, m)
| n == cPentOpLevel && l == [c3, c3]
= (c3 <^> c3 <^> c3, m)
| null cs && n == cTetrOpLevel && b == 2
= (a <^ a, m)
| null cs && n > cTetrOpLevel && b == 2
= if cGetHyperOp a == nM1
then (cApplyHy nM1 (a:(cGetHyperList a)) True, m)
else (cApplyHy nM1 [a,a] True, m)
| a == 2 &&
(
((null cs &&
((n == 5 && b == 3) ||
(n == 4 && b == 4))
) ||
(length cs == 1 && head cs == 2 && n == 4 && b == 2)
)
)
= (makeCanon (65536 :: Integer), m)
| a == 2 && b == 3 && null cs
= (cApplyHy nM1 [a, c4] True, m)
| a == 2 && b == 4 && null cs && n == 5
= (cApplyHy nM1 [a, cApplyHy n [a, b - 1] True] True, m)
| null cs &&
((a <= 6 && n == 4 && b == 3) ||
(a == 3 && n == 5 && b == 2))
= (a <^ a <^ a, m)
| ((lL > 2 && n > cMultOpLevel) || (lL >= 2 && n <= cMultOpLevel)) && sameVal l
= (promotedC, m)
| otherwise
= (cleanup b, m)
where nM1 = n - c1
hyperFree = not $ any cHyperExprAny l
sameVal (x:xs) = s' xs
where s' (v:vs) | v == x = s' vs
| otherwise = False
s' _ = True
sameVal _ = error "cHyperOp: List with at least two items expected"
(lL, lenC) = (length l, makeCanon $ toInteger lL)
promotedC = case n of
1 -> (head l) * lenC
2 -> (head l) <^ lenC
_ -> cApplyHy (n+1) [head l, lenC] True
cs' = if b == c1 then [] else cs
defHypEx = HX n l IntC
filterV v = (cApplyHy n (filter (/= v) l) False, m)
stripVs v = (cApplyHy n nl False, m)
where nl = if v == c0 then s l []
else (fst $ span (/= v) l)
s (x:xs) wl = if x == c0
then (if (ct0 xs 0) == 1
then wl
else (if length wl > 0 then init wl else [])
)
else s xs (wl ++ [x])
where ct0 (y:ys) ct = if y == c0 then ct0 ys (ct+1) else ct
ct0 _ ct = ct :: Integer
s _ _ = error "Logic error in strip0: should not get to the end"
cleanup (HX h cL@(a':e:xs) _)
| a == a' && h == n + c1 && null xs && null cs
= cApplyHy h [a, e + c1] False
| h == n && null cs = cApplyHy h (a:cL) False
| n == eL && cGetHyperOp a == mL = distProdForExpo
| otherwise = defHypEx
cleanup _
| n == eL && cGetHyperOp a == mL = distProdForExpo
| otherwise = defHypEx
distProdForExpo = computeExpr mL $ map (\p -> f (p:es)) $ cGetHyperList b'
where (b':es) = l
f l'@(x:xs) | cGetHyperOp x == eL = computeExpr eL [bX, eXEval * computeExpr eL xs]
| cHyperExprAny x = computeExpr eL l'
| otherwise = foldr1 (<^) l'
where (bX:eX) = cGetHyperList x
eXEval = computeExpr eL eX
f _ = error "Logic Error: Empty list found in cleanup"
(mL, eL) = (cMultOpLevel, cExpOpLevel)
cHyperOp h (a:_) m | h < c0 || not (cIntegral h) = error "cHyperOp: Hyper operator must be >= 0 and integral"
| otherwise = (a, m)
cHyperOp h l m | h < cAddOpLevel || not (cIntegral h)
= error "cHyperOp: Nullary value not defined if hyper operator is lower than addition"
| h == cAddOpLevel = (sum l, m)
| otherwise = (product l, m)
hyperLvlError :: Canon -> Canon -> String
hyperLvlError b n = "cHyperOp: Hyperexpr not defined when b < -1 and n is beyond exponentiation. b = " ++
show b ++ ", n = " ++ show n ++ "."
cFlattenAndGroup :: Canon -> [Canon]
cFlattenAndGroup c = cFlattenAndGroup' c cMaxExpoToExpand
cFlattenAndGroup' :: Canon -> Canon -> [Canon]
cFlattenAndGroup' c mx
| n1 == cAddOpLevel || n1 == cMultOpLevel = fAndG
| otherwise = [c]
where n1 = cGetHyperOp c
(cA,n,cL) = (abs c, cGetHyperOp cA, cGetHyperList cA)
fAndG = fmt (gF nH) (sF h)
where (gF, tF, sF) | n == cAddOpLevel = (sum, tryFlatSum, sortByHpo)
| otherwise = (product, tryFlatProd, id)
(h, nH) = partition cHyperExprAny $ concat $ map tF cL
fmt nonHC hyL | n == cAddOpLevel && nonHC == c0 = hyL
| n == cMultOpLevel && nonHC == c1 = hyL
| otherwise = (nonHC:hyL)
tryFlatSum v | cGetHyperOp v' == cAddOpLevel = cGetHyperList v'
| otherwise = [v']
where v' = fst $ cConvertToSum' v mx
tryFlatProd v | cGetHyperOp v == cMultOpLevel = cGetHyperList v
| otherwise = [v]
sortByHpo :: [Canon] -> [Canon]
sortByHpo l' | length l' == 1 = l'
| otherwise = filter (/= c0) $ map collHy $ groupBy (\x y -> snd x == snd y) $
sortBy sHPO $ map hpo $ map combineProd l'
where collHy z = combineSum $ computeExpr cAddOpLevel (map fst z)
type CanonInfo = ([Canon], ([[Canon]], [Canon]))
combineProd, combineSum :: Canon -> Canon
combineProd c | cGetHyperOp c' == cMultOpLevel = simpleHX c2 (map fst $ reverse $ sortBy sHPO $ map hpo $ cGetHyperList c')
| otherwise = c'
where c' = if cNegative c then negate aCm else aCm
where aCm = combine cMultOpLevel $ abs c
combineSum c = combine cAddOpLevel c
combine :: Canon -> Canon -> Canon
combine h c | length (cGetHyperList c) < 2 || cGetHyperOp c /= h
= c
| nH == nullary = computeExpr h cLc
| h == cAddOpLevel = computeExpr h (cLc ++ [nH])
| otherwise = computeExpr h (nH:cLc)
where (nH, cL') = (if h == cMultOpLevel then prepM else prepA) c
cLc = if null cL' then [] else (combine' (tail cL') [head cL'] [])
prepM (HX PoE (b:es) _) = (c1, [(b, computeExpr nxtOp es)])
prepM c'@(HX PoM _ _) = (fld nHe, concat $ map (snd . prepM) hE)
where (hE, nHe) = partition cHyperExpr $ cFlattenAndGroup c'
prepM c'@(HX _ _ _) = (c1, [(c', c1)])
prepM c' = (c', [])
prepA (HX PoM l _) = (c0, [(computeExpr nxtOp hE, product nHe)])
where (hE, nHe) = partition cHyperExpr l
prepA (HX PoA l _) = (fld nHe, concat $ map (snd . prepA) hE)
where (hE, nHe) = partition cHyperExpr l
prepA c'@(HX _ _ _) = (c0, [(c', c1)])
prepA c' = (c', [])
combine' l@((xB,xE):xs) (yP@(yB,yE):ys) wL
| xB == yB = combine' xs (wL ++ combinedTerm ++ ys) []
| otherwise = combine' l ys (yP:wL)
where combinedTerm = if (xE + yE == c0) then [] else [(xB, xE + yE)]
combine' (xP:xs) _ wL
= combine' xs (xP:wL) []
combine' _ y _
= map f y
where f (b,e) | e == c1 = b
| cGetHyperOp e == nxtOp = computeExpr nxtOp (b:cGetHyperList e)
| otherwise = computeExpr nxtOp [b, e]
(fld, nullary,nxtOp) | h == cAddOpLevel = (sum, c0, cMultOpLevel)
| otherwise = (product, c1, cExpOpLevel)
hpo :: Canon -> (Canon, CanonInfo)
hpo c' = (c', h' (abs c'))
where h' c@(HX PoM l2 _) = (getHyperBases c, (reverse $ sort $ concat e2, reverse $ sort $ filter (\h -> h /= c0) $ concat h2))
where (e2, h2) = unzip $ map spHyOp $ filter cHyperExpr l2
h' c@(HX _ _ _) = (getHyperBases c, spHyOp c)
h' _ = ([], ([[]], []))
getHyperBases :: Canon -> [Canon]
getHyperBases c = cGetBases' False False True c
sHPO :: (Canon, CanonInfo) -> (Canon, CanonInfo) -> Ordering
sHPO (_,(b1,(e1,hl1))) (_,(b2,(e2,hl2))) | hl1 > hl2 = LT
| hl1 < hl2 = GT
| otherwise = case compare hl2 hl1 of
EQ -> case compare b1 b2 of
EQ -> case compare (length e1) (length e2) of
EQ -> compare e1 e2
cmpE -> cmpE
cmpB -> cmpB
cmp -> cmp
spHyOp :: Canon -> ([[Canon]], [Canon])
spHyOp c | h == cExpOpLevel && (cHyperExpr $ head hL) = (replicate nR $ tHl, replicate nR hH)
| h == cMultOpLevel = (filter (/= []) $ sort $ concat e', sort $ concat h')
| otherwise = ([tHl], [h])
where (h, hL) = (cGetHyperOp c, cGetHyperList c)
hH = cGetHyperOp $ head hL
nR = if l > 2 || e > 1000 then 1000 else (fromInteger $ cToI e)
where l = length $ cGetHyperList $ head hL
e = head $ tail hL
(e', h') = unzip $ map spHyOp $ filter cHyperExpr hL
tHl | h == cExpOpLevel && length bHl > 1 = tail bHl
| otherwise = if length hL > 1 then tail hL else []
where bHl = cGetHyperList $ head hL
cHyperOpSpecial :: Integer -> Canon -> Canon -> CycloMap -> (Canon, CycloMap)
cHyperOpSpecial 0 Pc0 b' m = cAdd b' c1 m
cHyperOpSpecial 1 Pc0 b' m = (b', m)
cHyperOpSpecial 2 Pc0 _ m = (c0, m)
cHyperOpSpecial 3 Pc0 b' m = (if b' == c0 then c1 else c0, m)
cHyperOpSpecial _ Pc0 b' m = (if cOdd b' then c0 else c1, m)
cHyperOpSpecial 0 _ Pc0 m = (c1, m)
cHyperOpSpecial 1 a' Pc0 m = (a', m)
cHyperOpSpecial 2 _ Pc0 m = (c0, m)
cHyperOpSpecial _ _ Pc0 m = (c1, m)
cHyperOpSpecial 0 _ PcN1 m = (c0, m)
cHyperOpSpecial 1 a' PcN1 m = cSubtract a' c1 m
cHyperOpSpecial 2 a' PcN1 m = (cNegate a', m)
cHyperOpSpecial 3 a' PcN1 m = (cReciprocal a', m)
cHyperOpSpecial _ _ PcN1 m = (c0, m)
cHyperOpSpecial h Pc2 Pc2 m | h == 0 = (smallCanons !! 3, m)
| otherwise = (smallCanons !! 4, m)
cHyperOpSpecial _ Pc1 _ m = (c1, m)
cHyperOpSpecial _ a' Pc1 m = (a', m)
cHyperOpSpecial _ _ _ _ = error "Can't compute this hyperoperation. b must be >= -1. Not a 'special' case"
cGetHyperList :: Canon -> [Canon]
cGetHyperList (HX _ cL _) = cL
cGetHyperList _ = []
cGetHyperOp :: Canon -> Canon
cGetHyperOp (HX h _ _) = h
cGetHyperOp _ = c0
infixr 9 <^, >^
(<^), (>^) :: Canon -> Canon -> Canon
a <^ b = fst $ cExp a b True crCycloInitMap
r >^ n = cRoot n r
cToCR :: Canon -> CR_
cToCR (Can c v) | v /= IrrC = gcrToCR c
| otherwise = error "cToCR: Cannot convert irrational canons to underlying data structure"
cToCR (Bare 1 _ ) = cr1
cToCR (Bare n NSim) = fst $ crFromI n
cToCR (Bare n Simp) = [(n,1)]
cToCR c = gcrToCR $ cToGCR c
gcrToC :: GCR_ -> Canon
gcrToC g | gcrBare g = Bare (gcrToI g) Simp
| otherwise = Can g (gcrCVT g)
gcrCVT :: GCR_ -> CanonValueType
gcrCVT POne = IntC
gcrCVT g = g' g IntC
where g' _ IrrC = IrrC
g' POne v = v
g' ((_,ce):cs) v = g' cs (dcv v ce)
g' _ _ = error "gcrCVT : Logic error. Patterns should have been exhaustive"
dcv IrrC _ = IrrC
dcv _ (Can _ IrrC) = IrrC
dcv _ (Can _ NirC) = IrrC
dcv IntC (Bare n _) = if n < 0 then NirC else IntC
dcv v (Bare _ _) = v
dcv v c = if cNegative c then NirC else v
cN1, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9 :: Canon
(cN1:c0:c1:c2:c3:c4:c5:c6:c7:c8:c9:_) = map makeCanon [-1..maxHyperOpDispLevel]
impossibleHyperValue :: Canon
impossibleHyperValue = cN1
cToI :: Canon -> Integer
cToI (Bare i _ ) = i
cToI c@(Can g v) | v == IntC && (cHyperExpr c || cSuperLogGT (fst $ cSuperLog c) cSuperLogCutoff)
= error $ tooBigError c
| v == IntC = gcrToI g
| otherwise = error $ nonIntError c
cToI h@(HX _ _ v) | v == IntC = error $ tooBigError h
| otherwise = error $ nonIntError h
tooBigError, nonIntError :: Canon -> String
tooBigError c = "This expression is too large to be converted to an integer: " ++ show c
nonIntError c = "Can't convert a non-integral canon to an integer: " ++ show c
cToD :: Canon -> Double
cToD (Bare i _) = fromIntegral i
cToD (Can c _) = gcrToD c
cToD (HX _ _ _) = error "This hyper-expression is too large to convert to a double"
cMult :: Canon -> Canon -> CycloMap -> (Canon, CycloMap)
cMult Pc0 _ m = (c0, m)
cMult _ Pc0 m = (c0, m)
cMult Pc1 y m = (y, m)
cMult x Pc1 m = (x, m)
cMult x y m | not (cHyperExprAny x) && not (cHyperExprAny y)
= (gcrToC g, m')
| otherwise = (multH x y, m)
where (g, m') = gcrMult (cToGCR x) (cToGCR y) m
cAdd, cSubtract :: Canon -> Canon -> CycloMap -> (Canon, CycloMap)
cAdd = cApplyAdtvOp True
cSubtract = cApplyAdtvOp False
cApplyAdtvOp :: Bool -> Canon -> Canon -> CycloMap -> (Canon, CycloMap)
cApplyAdtvOp _ x Pc0 m = (x, m)
cApplyAdtvOp True Pc0 y m = (y, m)
cApplyAdtvOp False Pc0 y m = (negate y, m)
cApplyAdtvOp b x y m | not b && x == y = (c0, m)
| b && x == (negate y) = (c0, m)
| not hax && not hay = (r, m')
| otherwise = (addH x (if b then y else negate y), m)
where (hax, hay) = (cHyperExprAny x, cHyperExprAny y)
gcd' = cGCD x y
(x', y') = (x / gcd', y / gcd')
r | tooBigToAdd x' || tooBigToAdd y'
= simpleHX cAddOpLevel [x, if b then y else (negate y)]
| otherwise = gcd' * (crToC c False)
(c, m') = crApplyAdtvOptConv b (cToCR x') (cToCR y') m
tooBigToAdd :: Canon -> Bool
tooBigToAdd c@(Can _ _) | cHyperExprAny c = True
| otherwise = cSuperLogGT (fst $ cSuperLog c) cSuperLogCutoff
tooBigToAdd (HX _ _ _) = True
tooBigToAdd (Bare _ _) = False
cExp :: Canon -> Canon -> Bool -> CycloMap -> (Canon, CycloMap)
cExp c e b m | cNegative e && (not b)
= error "Per param flag, negative exponentiation is not allowed here."
| cIrrational c && cIrrational e
= error "cExp: Raising an irrational number to an irrational power is not currently supported."
| otherwise = cExp' c e
where cExp' _ Pc0 = (c1, m)
cExp' Pc1 _ = (c1, m)
cExp' Pc0 _ | cNegative e = error "0^e where e < 0 gives a div by zero error"
| otherwise = (c0, m)
cExp' _ Pc1 = (c, m)
cExp' _ _ | cHyperExprAny c || cHyperExprAny e = (cApplyHy cExpOpLevel [c,e] True, m)
| otherwise = (gcrToC g, mg)
where (g, mg) = gE (cToGCR c) e m
gE g' e' m' | gcrNegative g'
= case cValueType e' of
IntC -> if cOdd e' then (gcreN1:absTail, m'')
else (absTail, m'')
NirC -> if cOdd d then (gcreN1:absTail, m'')
else error "gE: Imaginary numbers not supported"
IrrC -> error "gE: Raising neg numbers to irr. powers not supported"
| otherwise
= f g' m'
where (absTail, m'') = gE (gcrAbs g') e' m'
(_, d) = cSplit e'
f [] mf = ([], mf)
f ((p,x):gs) mf = (fp, mf')
where (prd, mx) = cMult e' x mf
(t, mn) = f gs mx
(fp, mf') = gcrMult [(p, prd)] t mn
cNegative, cPositive :: Canon -> Bool
cNegative (Bare n _ ) = n < 0
cNegative (Can c _ ) = gcrNegative c
cNegative (HX PoA cL _) | lp == 0 = True
| ln == 0 = False
| otherwise = (cCmp pH nH == LT)
where (posCL, negCL') = partition cPositive cL
negCL = map negate negCL'
lp = length posCL
ln = length negCL
pH = cApplyHy cAddOpLevel posCL True
nH = cApplyHy cAddOpLevel negCL True
cNegative (HX PoM cL _) = cNegative $ head cL
cNegative (HX _ _ _) = False
cPositive (Bare n _ ) = n > 0
cPositive (Can c _ ) = gcrPositive c
cPositive h@(HX PoA _ _) = not $ cNegative h
cPositive h@(HX PoM _ _) = not $ cNegative h
cPositive (HX _ _ _) = True
cNegate, cAbs, cSignum :: Canon -> Canon
cNegate (Bare 0 _) = c0
cNegate (Bare 1 _) = cN1
cNegate (Bare (-1) _) = c1
cNegate (Bare x Simp) = Can (gcreN1 : [(x, c1)]) IntC
cNegate (Bare x NSim) = Bare (-1 * x) NSim
cNegate (Can x v) = gcrNegateCanonical x v
cNegate h@(HX PoA cL _) | cNegative h = simpleHX cAddOpLevel (reverse $ map negate cL)
| otherwise = simpleHX cMultOpLevel [cN1,h]
cNegate (HX PoM cL _) | hD == cN1 = cApplyHy cMultOpLevel (tail cL) True
| nhH && cNegative hD = simpleHX cMultOpLevel ((abs hD):(tail cL))
| nhH && cPositive hD = simpleHX cMultOpLevel ((negate hD):(tail cL))
| otherwise = simpleHX cMultOpLevel (cN1:cL)
where (hD, nhH) = (head cL, not $ cHyperExpr hD)
cNegate he@(HX _ _ _) = simpleHX cMultOpLevel [cN1, he]
cAbs x | cNegative x = cNegate x
| otherwise = x
cSignum (Bare 0 _) = c0
cSignum g | cNegative g = cN1
| otherwise = c1
cLGApply :: (GCR_ -> GCR_ -> GCR_) -> Canon -> Canon -> Canon
cLGApply f x y | cNegative x ||
cNegative y = gcrToC $ f (cToGCR $ cAbs x) (cToGCR $ cAbs y)
| otherwise = gcrToC $ f (cToGCR x) (cToGCR y)
tryToCanonizeHyperExpr :: Canon -> Maybe Canon
tryToCanonizeHyperExpr c@(HX _ _ _)
| cHyperSumAny c || cMaxHyperOp c > cTetrOpLevel || cMaxTetrLevel > 10 = Nothing
| otherwise = Just $ conv c
where cMaxTetrLevel = mtl c0 c
mtl wM (HX h l@(x:xs) _) | h == cTetrOpLevel = foldl1 max [wM, mtl wM x, cApplyHy h xs True]
| otherwise = foldl1 max (wM:(map (mtl wM) l))
mtl wM _ = wM
conv (HX PoM l _) = product $ map conv l
conv (HX PoE l _) = foldr1 (<^) $ map conv l
conv (HX h l@(b:x:_) _) | h /= c4 = error "Logic error: Only tetration allowed here"
| length l /= 2 = error "Logic error: Tetration list must only have two elements"
| hB < c4 = convToTwr l
| hB == c4 && simpleHyperExpr b && cMaxTetrBase <= 10
= nestedTetr x
| otherwise = foldr1 (<^) $ replicate (fromInteger $ cToI $ l !! 1) (conv $ l !! 0)
where (hB, bHl, bT) = (cGetHyperOp b, cGetHyperList b, convToTwr bHl)
cMaxTetrBase = mtl c0 b
convToTwr l' = foldr1 (<^) $ replicate (fromInteger $ cToI $ l' !! 1) (conv $ l' !! 0)
nestedTetr oe | oe == 2 = b <^ b
| otherwise = bT <^ nestedTetr (oe - 1)
simpleHyperExpr c'@(HX h' l' _) | h' < cTetrOpLevel || not (cIntegral c') ||
length l' /= 2 || not (any cHyperExprAny l')
= True
| otherwise = False
simpleHyperExpr _ = False
conv c' = c'
tryToCanonizeHyperExpr c = Just c
cDiv :: Canon -> Canon -> CycloMap -> (Canon, CycloMap)
cDiv _ Pc0 _ = error "cDiv: Division by zero error"
cDiv Pc0 _ m = (c0, m)
cDiv x y m
| not (cHyperExprAny x) && not (cHyperExprAny y)
= cMult (cReciprocal y) x m
| y' == c1 = (x', m)
| otherwise = case tryHyperDiv x y m of
Right r -> r
Left s -> error s
where (x', y') = reduceProds x y
tryHyperDiv :: Canon -> Canon -> CycloMap -> Either String (Canon, CycloMap)
tryHyperDiv x y m
| fmx /= hyDef && fmy /= hyDef
= if (cCanonical fQ && cIntegral fQ && cHyperExprAny fQ)
then Right (cConvertToHyperExpr fQ, m')
else Right (fQ, m')
| otherwise
= Left ("At this time, one can only divide hyper expressions when x is a multiple of y, non-sums or limited tetrations: x = "
++ show x ++ ", y = " ++ show y)
where fmch v = fromMaybe hyDef (tryToCanonizeHyperExpr v)
(hyDef, fmx, fmy) = (impossibleHyperValue, fmch x, fmch y)
(fQ, m') = cDiv fmx fmy m
cConvertToHyperExpr :: Canon -> Canon
cConvertToHyperExpr c | not (cIntegral c) = error "Cannot convert a non-integral canon to a hyper expression"
| cCanonical c && cHyperExprAny c = cApplyHy cMultOpLevel ((product nHe):hE) False
| otherwise = c
where (hE, nHe) = partition cHyperExpr $ map hF $ cToGCR c
hF (p, e) = if e == c1 then pC else (cApplyHy cExpOpLevel [pC, e] True) where pC = makeCanon p
cReciprocal :: Canon -> Canon
cReciprocal x | not (cHyperExprAny x) = fst $ cExp x cN1 True crCycloInitMap
| fmx /= hyDef = cReciprocal fmx
| otherwise = error $ "At this time, one can only take reciprocals of hyper expressions which are " ++
"non-sums and limited tetrations."
where fmch v = fromMaybe hyDef (tryToCanonizeHyperExpr v)
(hyDef, fmx) = (impossibleHyperValue, fmch x)
cIntegral, cIrrational, cRational, cSimplified, cPrime, cIsPrimeTower :: Canon -> Bool
cIntegral c = cValueType c == IntC
cIrrational c = cValueType c == IrrC
cRational c = not $ cIrrational c
cSimplified (Bare _ Simp) = True
cSimplified (Bare _ NSim) = False
cSimplified (Can c _) = gcrSimplified c
cSimplified c@(HX h l _) = h /= cAddOpLevel && ((cHyperProd c && all cSimplified l) || (cSimplified $ head l))
cPrime c = cSimplified c && c > c1
cIsPrimeTower c = cPrimeTowerLevel c > 0
cHyperExpr, cHyperExprAny, cHyperSum, cHyperSumAny, cHyperProd, cHyperExpo :: Canon -> Bool
cHyperExpr = cHyperPredCheck (>= cAddOpLevel) False
cHyperExprAny = cHyperPredCheck (>= cAddOpLevel) True
cHyperSum (HX h (j:k:cs) _) = h == cAddOpLevel ||
(h == cMultOpLevel && j == cN1 && cGetHyperOp k == cAddOpLevel && null cs)
cHyperSum _ = False
cHyperSumAny = cHyperPredCheck (== cAddOpLevel) True
cHyperProd c@(HX PoM _ _) = not $ cHyperSum c
cHyperProd _ = False;
cHyperExpo = cHyperPredCheck (== cExpOpLevel) False
cHyperPredCheck :: (Canon -> Bool) -> Bool -> Canon -> Bool
cHyperPredCheck f b c | f (cGetHyperOp c) = True
| not b = False
| otherwise = cHP' c
where cHP' (HX _ l _) = any (cHyperPredCheck f b) l
cHP' (Can g _) = any (cHyperPredCheck f b) $ map snd g
cHP' _ = False
cNumerator, cDenominator :: Canon -> Canon
cNumerator (Can c _ ) = gcrToC $ filter (\x -> cPositive $ snd x) c
cNumerator b = b
cDenominator (Can c _ ) = gcrToC $ map (\(p,e) -> (p, cN1*e)) $ filter (\(_,e) -> cNegative e) c
cDenominator _ = c1
cNestExpTail :: Canon -> Bool -> Canon
cNestExpTail c'@(HX h (b:xs) IntC) bF
| h == cAddOpLevel= c1
| h < cExpOpLevel = error errorMsg
| otherwise = if bF then baseTail b * eH else eH
where expTail = cApplyHy h xs True
eH = expRec h expTail
expRec h' e' | h' == cExpOpLevel = e'
| otherwise = expRec (h' - c1) newE
where newE = cApplyHy h' [b, e' - c1] True
baseTail (HX PoA _ _) = c1
baseTail c@(HX PoM _ _) = error $ nonPrimePowerError c
baseTail c@(HX _ _ _) = cNestExpTail c bF
baseTail c@(Can g _) | length g > 1 = error $ nonPrimePowerError c
| otherwise = snd $ head g
baseTail (Bare _ Simp) = c1
baseTail c = error $ nonPrimePowerError c
errorMsg = "nestedExpTail: requires a hyper expression at level >= exponentiation: " ++ show c'
cNestExpTail _ _ = c1
cCleanup :: Canon -> Canon
cCleanup = cHyperize . cQuasiCanonize
cCleanupAsNumDenPair :: Canon -> (Canon,Canon)
cCleanupAsNumDenPair c = (n,d)
where (n, d) = (cHyperize $ pr nL, cHyperize $ pr dL)
qc = cQuasiCanonize c
pr cL = simpleHX cMultOpLevel $ map expDemote $ filter (\(_, e) -> e /= c0) cL
(nL, dL) = unzip $ map (\(p,(eP,eN)) -> ((p, eP), (p, eN)) ) $ map (\(p,e) -> (p, spl e)) $ map (\c' -> expPromote c') $ cGetFactors qc
spl c' = (simpleHX cAddOpLevel pos, simpleHX cAddOpLevel (map negate neg))
where (pos, neg) = partition cPositive $ cGetAddends c'
cHyperize :: Canon -> Canon
cHyperize c | null oM = hyperize' c
| null iM = c
| otherwise = simpleHX cMultOpLevel (iMp : oM)
where (oM, iM) = partition (\m -> cMaxHyperOpForQC m > maxHyperOpDelveLevel) $ cGetFactors c
iMp = hyperize' $ simpleHX cMultOpLevel iM
hyperize' :: Canon -> Canon
hyperize' c | not (cQuasiCanonized c) || (h /= cExpOpLevel && h /= cMultOpLevel) || null iM
= c
| any cNegative $ concat $ map (\(_,e) -> cGetAddends e) $ map expPromote $ cGetFactors c
= c
| not (null $ cGetBases' False True False $ simpleHX cMultOpLevel iM)
= c
| not (foldl1 (&&) $ map snd process)
= c
| null grp = c
| not (foldl1 (&&) $ map (\(_,l) -> allTheSame $ map snd l) grp)
= c
| null grp' || not (foldl1 (&&) $ map snd grp')
= c
| otherwise = iSp * oSp
where h = cGetHyperOp c
(iM, oM) = partition (\m -> cGetHyperOp m == cExpOpLevel) $ cGetFactors c
process = map (\l -> hypMap (l !! 0, l !! 1)) $ map cGetHyperList iM
oSp = product (oM ++ (map (snd . snd . fst) process))
grp = grpExpr $ concat $ map (\((p,(eMap,_)),_) -> map (\((_,t),m)-> (t, (p, m))) eMap) process
grp' = map (\(e,l) -> ((e, snd $ head l), cBaseRadical e == product (map fst l))) grp
iSp = product $ map (\((e,m),_) -> cApplyHy cExpOpLevel [e, m] True) grp'
allTheSame l@(x:_:_) = and $ map (== x) (tail l)
allTheSame _ = True
grpExpr l@(_:_:_) = gE' l []
grpExpr ((e,p):_) = [(e, [p])]
grpExpr _ = []
gE' l@((xf,_):_) wL = gE' nM ((xf, map snd m):wL)
where (m,nM) = partition (\e -> xf == fst e) l
gE' _ wL = wL
hypMap :: (Canon, Canon) -> ((Canon, ([((Canon, Canon), Canon)], Canon)), Bool)
hypMap (p, e) = ((p, (mV', osProd)), not $ any (\((_,t),_) -> t == impossibleHyperValue) mV')
where (iS, oS) = partition candPred $ cGetAddends e
osProd = computeExpr cMultOpLevel (map (\x -> p <^ x) oS)
mV' = mV p iS
mV :: Canon -> [Canon] -> [((Canon, Canon), Canon)]
mV p iS | null iS = []
| otherwise = map (hypCheck p) $ cGetAddends $ grpAndSrtList p iS
hypCheck :: Canon -> Canon -> ((Canon, Canon), Canon)
hypCheck p c = ((c, liftedTail), p')
where (fs, base) = (cGetFactors c, head $ cGetHyperList $ head $ fs)
liftedTail | b' /= c1 = impossibleHyperValue
| otherwise = tryLiftTail $ head fs
(_, p', b') = simpleReduce (product $ tail fs) (qcBase p base) False
grpAndSrtList :: Canon -> [Canon] -> Canon
grpAndSrtList p iS = simpleHX cAddOpLevel $ map s $ cGetAddends $ factorSumIter True $ simpleHX cAddOpLevel $ map m' iS
where m' a = applyFcnForHy a cMultOpLevel cFlattenAndGroup
s a = applyFcnForHy a cMultOpLevel (srt p)
srt :: Canon -> Canon -> [Canon]
srt p a' = ((reverse $ sortOn (nestLevel p) cs) ++ ncs)
where (cs, ncs) = partition (\e -> candPred e && elem p (cGetBases e)) $ cGetFactors a'
candPred :: Canon -> Bool
candPred c'@(HX PoM _ _) = any candPred $ cGetHyperList c'
candPred c' = powSq c' || cGetHyperOp c' == cTetrOpLevel
where powSq (HX PoE (b:e:xs) _) = null xs && b == e
powSq _ = False
tryLiftTail :: Canon -> Canon
tryLiftTail c | cGetHyperOp c < cExpOpLevel || length l < 2 || cGetHyperOp cLift == cExpOpLevel
= impossibleHyperValue
| otherwise = cLift
where (l, b, cLift) = (cGetHyperList c, head l, cApplyHy cExpOpLevel [b, c] True)
qcBase :: Canon -> Canon -> Canon
qcBase p c@(Bare n _) = if (n == cToI p) then c1 else error (errMsgQCB p c [])
qcBase p c@(Can g _) | cHyperExpr p || length pe == 0 = error (errMsgQCB p c [])
| otherwise = snd $ head pe
where pe = filter (\(p',_) -> p' == pI) g
pI = cToI p
qcBase p c | cGetHyperOp p == cAddOpLevel && p == c
= c1
| length qce == 1 = snd $ head qce
| otherwise = error $ errMsgQCB p c qce
where qce = filter (\(b,_) -> p == b) $ map expPromote $ cGetFactors $ cQuasiCanonize c
errMsgQCB :: Canon -> Canon -> [(Canon, Canon)] -> String
errMsgQCB p c qce = "Logic error in qcBase: Canon: " ++ show c ++ " did not contain p = " ++
show p ++ ". Length = " ++ show (length qce)
nestLevel :: Canon -> Canon -> Int
nestLevel p c | cBare p || cGetHyperOp p == cAddOpLevel
= nL c 0
| otherwise = error $ "nestLevel: Only for a prime or sum: " ++ show p ++ " when checking: " ++ show c
where nL c'@(HX PoA l _) lvl = if p == c' then (lvl + 1) else (getMax l lvl)
nL (HX PoM l _) lvl = getMax l lvl
nL (HX _ (b:_) _) lvl = nL b (lvl+1)
nL (Bare n _) lvl = if cHyperExpr p || n /= pI then badLevel else lvl
nL (Can g _) lvl | cHyperExpr p || null matchP = badLevel
| otherwise = if x == 1 then lvl else (lvl + 1)
where matchP = filter (\(p',_) -> p' == pI) g
x = snd $ head matchP
nL _ _ = error "Should not reach this point in the code"
pI = cToI p
badLevel = -1
getMax l lvl = if null l then 0 else (foldl1 max $ map (\m -> nL m lvl) l)
simpleHX :: Canon -> [Canon] -> Canon
simpleHX h c | ln == 1 = head c
| ln > 1 = HX h c IntC
| otherwise = if h == cAddOpLevel then c0 else c1
where ln = length c
cGetAddends :: Canon -> [Canon]
cGetAddends c = if cGetHyperOp c == cAddOpLevel then cGetHyperList c else [c]
cGetFactors :: Canon -> [Canon]
cGetFactors c = if cGetHyperOp c == cMultOpLevel then cGetHyperList c else [c]
cDelve :: Canon -> [Int] -> Canon
cDelve c xL | x < length hL = if null xs then e else cDelve e xs
| otherwise = error $ "Can not find index " ++ show x ++ " in canon: " ++ show c
where (hL, e) = (cGetHyperList c, hL !! x)
(x:xs) = xL
applyFcnForHy :: Canon -> Canon -> (Canon -> [Canon]) -> Canon
applyFcnForHy c h f = if cGetHyperOp c == h then simpleHX h (f c) else c
factorSumIter :: Bool -> Canon -> Canon
factorSumIter hF (HX PoA hL _) = fs (head hL) (tail hL) []
where fs a (x:xs) wL | gcd' == c1 || (hF && not (cHyperExpr gcd')) = fs a xs (wL ++ [x])
| otherwise = fs nv xs wL
where (nv, gcd') = simpleFactorPair a x hF
fs a _ wL = computeExpr cAddOpLevel (wL ++ [a])
factorSumIter _ c = c
cQuasiCanonized :: Canon -> Bool
cQuasiCanonized (HX PoA _ _) = True
cQuasiCanonized c@(HX PoM l _) = all cQuasiCanonized l && null (cGetBases' True True False c)
cQuasiCanonized (HX PoE (b:_:xs) _) = (cBare b || cGetHyperOp b == cAddOpLevel) && null xs
cQuasiCanonized c@(HX _ _ _) = cMaxHyperOpForQC c > maxHyperOpDelveLevel
cQuasiCanonized _ = True
cQuasiCanonize :: Canon -> Canon
cQuasiCanonize c | (not pF && cMaxHyperOpForQC c > maxHyperOpDelveLevel) ||
(pF && null sM)
= c
| pF && not (null bM)
= computeExpr cMultOpLevel ((cL sMp) ++ bM)
| otherwise = computeExpr cMultOpLevel (cL c)
where (bM, sM) = partition (\m -> cMaxHyperOpForQC m > maxHyperOpDelveLevel) $ cGetHyperList c
(sMp, pF) = (computeExpr cMultOpLevel sM, cGetHyperOp c == cMultOpLevel)
cL c' = map (\l -> promote (fst $ head l, sum $ map snd l)) $
groupBy (\x y -> fst x == fst y) $ sortOn fst $ can' c'
promote (b',e') | e' == c1 = b'
| cHyperExprAny e' = (computeExpr cExpOpLevel [b',e'])
| otherwise = b' <^ e'
can' c'@(HX h l'@(b:xs) IntC)
| h == cAddOpLevel = [(c', c1)]
| h == cMultOpLevel = concat $ map can' l'
| otherwise = map (\(b', e') -> (b', mul e')) $ can' b
where expTail = cApplyHy h xs True
eH = expRec h expTail
mul :: Canon -> Canon
mul e' | e' == c1 = eH
| eH == c1 = e'
| otherwise = applyFcnForHy (simpleHX cMultOpLevel [e', eH]) cMultOpLevel cFlattenAndGroup
expRec h' e' | h' == cExpOpLevel = e'
| otherwise = expRec (h' - c1) newE
where newE = cApplyHy h' [b, e' - c1] True
can' (Can g _) = map (\(b', e') -> (makeCanon b', e')) g
can' c' = [(c', c1)]
nonPrimePowerError :: Canon -> String
nonPrimePowerError c = "cNestExpTail: Can't compute if base is a product or unfactored: " ++ show c
cBaseRadical :: Canon -> Canon
cBaseRadical = cBaseRadical' False False False
cBaseRadical' :: Bool -> Bool -> Bool -> Canon -> Canon
cBaseRadical' f d h c = product $ cGetBases' f d h c
cHyOpLvlOutOfRange :: Canon -> Bool
cHyOpLvlOutOfRange h | h < cMultOpLevel || h > maxHyperOpDelveLevel = True
| otherwise = False
data FuncType = Mult | Gcd | Lcm deriving (Show, Eq)
cMultiplicative :: Canon -> Canon -> FuncType -> [Canon]
cMultiplicative v w t
| not (cHyperExpr v) && not (cHyperExpr w)
= case t of
Gcd -> [gvw, div v gvw, div w gvw]
Lcm -> [cLCM v w]
_ -> [v * w]
| t == Mult = [v * w]
| t == Lcm && relPrime = if v' == c1 then [abs w]
else (if w' == c1 then [abs v]
else [h' $ cCleanup $ head $ cMultiplicative' vA wA t])
| t == Lcm = [h' $ cCleanup $ head $ cMultiplicative' (cQuasiCanonize vA) (cQuasiCanonize wA) t]
| t == Gcd && relPrime = [gHvw, f v' v, f w' w]
| otherwise = [gHvw', f v2 v, f w2 w]
where gvw = cGCD v w
(vA, wA) = (abs v, abs w)
h' c = simpleHX cMultOpLevel (concat $ map (\e -> cGetFactors $ if cQuasiCanonized e then cHyperize e else e) $ cGetFactors c)
(gHvw:v':w':_) = map (h' . cCleanup) $ cMultiplicative' vA wA Gcd
(gHvw2:v2:w2:_) = map (h' . cCleanup) $ cMultiplicative' (cQuasiCanonize v') (cQuasiCanonize w') Gcd
gHvw' = gHvw * gHvw2
relPrime = null $ intersect (cGetBases v') (cGetBases w')
f a' a = if signum a == cN1 then negate a' else a'
cMultiplicative' :: Canon -> Canon -> FuncType -> [Canon]
cMultiplicative' v w t = [apply r, apply xN, apply yN]
where
apply l = product $ (ccProd:hEs)
where ccProd = product $ map conv cc
where conv c | cHyperExpr c = (head hL) <^ (head $ tail hL)
| otherwise = c
where hL = cGetHyperList c
(cc, hEs) = partition canConv l
canConv c = not (cHyperExprAny c) ||
(cGetHyperOp c == cExpOpLevel && length hL == 2 && not (any cHyperExprAny hL))
where hL = cGetHyperList c
(xN, yN, r) = p' (allFactors v) (allFactors w)
p' x y
| eitherNull x1 y1 = if t == Gcd then (x1, y1, r1) else ([], [], r1 ++ x1 ++ y1)
| eitherNull x2 y2 = if t == Gcd then (x2, y2, r2) else ([], [], r2 ++ x2 ++ y2)
| eitherNull x3 y3 = if t == Gcd then (x3, y3, r3) else ([], [], r3 ++ x3 ++ y3)
| otherwise = if t == Gcd then (x4, y4, r4) else ([], [], r4 ++ x4 ++ y4)
where (x1, y1, r1) = rW x y [] False False
(x2, y2, r2) = rW x1 y1 r1 True False
(x3, y3, r3) = rW x2 y2 r2 False True
(x4, y4, r4) = rW x3 y3 r3 True True
rW x' y' g' bx by = promote (fmt x') (fmt y') [] [] g' bx by t
where fmt m = allFactors (simpleHX cMultOpLevel m)
eitherNull j k = null j || null k
promote a' b' aW' bW' g' aF bF t'
= if (aF && bF) then r' (sortOn getBase' a') (sortOn getBase' b') aW' bW' g' [] []
else r' a' b' aW' bW' g' [] []
where
r' (a:as) (b:bs) aW bW g aN bN
| aB == bB && (aT /= impossibleHyperValue && bT /= impossibleHyperValue)
= r' as bs nAw nBw (g ++ [aB <^ m]) aN bN
| otherwise = r' (a:as) bs aW bW g aN (bN ++ [b])
where (aB, bB) = (f' a aF, f' b bF)
(aT, bT) = (e' a aF, e' b bF)
m | t' == Gcd && aF && bF = if a < b then aT else bT
| t' == Gcd = min aT bT
| t' == Lcm && aF && bF = if a > b then aT else bT
| t' == Lcm = max aT bT
| otherwise = aT + bT
nGcdE w' | w' /= m && cMaxHyperOp w' > cTetrOpLevel && cMaxHyperOp m > cTetrOpLevel
= simpleHX cAddOpLevel [w', negate m]
| otherwise = w' - m
(nAw, nBw) | t' == Gcd = (aW ++ [aB <^ nGcdE aT], bW ++ [bB <^ nGcdE bT])
| otherwise = (aW, bW)
r' (a:as) _ aW bW g aN bN
= r' as bN newAw bW g newAn []
where (newAw, newAn) | t' == Gcd = (aW ++ [a], aN)
| otherwise = (aW, aN ++ [a])
r' _ b aW bW g aN bN
| t' == Gcd = (aW, bW ++ b ++ bN, g)
| otherwise = (aN, b ++ bN, g)
f' j fB = if fB then getBase' j else j
e' j fB = if fB then (if cGetHyperOp j > maxHyperOpDelveLevel
then impossibleHyperValue
else cNestExpTail j False)
else c1
getBase' s@(HX PoA _ _) = s
getBase' c@(HX PoM _ _) = error $ getBaseHypError c
getBase' (HX _ (b:_) _) = b
getBase' (Bare b _) = makeCanon b
getBase' c@(Can g _) | length g == 1 = gcrToC g
| otherwise = error $ getBaseCanError c
getBase' _ = error "getBase' unknown canon type encountered."
getBaseCanError c = "getBase': canonical param with > 1 base not allowed: " ++ show c
getBaseHypError c = "getBase': hyper prod param not allowed: " ++ show c
allFactors :: Canon -> [Canon]
allFactors (HX PoM l _) = concat $ map allFactors l
allFactors c@(HX _ _ _) = [c]
allFactors c | cIntegral c = filter (/= c1) $ map expLift $ cToGCR c
| otherwise = error "allFactors only takes integral canons"
where expLift (b', e') = if e' == c1 then bC else (simpleHX cExpOpLevel [bC,e'])
where bC = makeCanon b'
cRoot :: Canon -> Canon -> Canon
cRoot c r
| not (cPositive r) = error "r-th roots are not allowed when r <= 0 or not integral"
| r == c1 || c == c0 = c
| cNegative c && cEven r = error "cRoot does not support imaginary numbers (even roots of negative numbers)."
| all (\(_,e) -> cMod e r == 0) cL'
= if cNegative c then negate root else root
| cMaxHyperOp c > maxHyperOpDelveLevel
= error $ "Root could not be found but that may be due to the level of hyper operation being beyond the cutoff: " ++ show c
| otherwise = error $ "The root requested was not a multiple of all the exponents in the expansion of " ++ show c
where cL' = map expPromote $ allFactors $ cQuasiCanonize $ abs c
root = cCleanup $ simpleHX cMultOpLevel $ map (\(p,e) -> expDemote (p, e / r)) cL'
cPrimeTowerLevel :: Canon -> Canon
cPrimeTowerLevel (Bare _ Simp) = c1
cPrimeTowerLevel (Can g IntC) | gcrPrimePower g = cPrimeTowerLevelI (snd $ head g) (fst $ head g) (1 :: Integer)
| otherwise = c0
cPrimeTowerLevel c@(HX h l@(b:xl) _) | h < cExpOpLevel || any cHyperExprAny l || not (cPrime b)
= c0
| h == cExpOpLevel = if cQuasiCanonized c && cMaxHyperOp c > cExpOpLevel
then (cPrimeTowerLevel $ cHyperize c)
else (makeCanon $ toInteger $ length l)
| h == cTetrOpLevel = simpleHX h xl
| cMaxHyperOpForQC c <= maxHyperOpDelveLevel
= cDelve (cQuasiCanonize c) [1,1]
| otherwise = c
cPrimeTowerLevel _ = c0
cPrimeTowerLevelI :: Canon -> Integer -> Integer -> Canon
cPrimeTowerLevelI (Bare b _) n l | b == n = makeCanon $ l + 1
| otherwise = c0
cPrimeTowerLevelI (Can g IntC) n l | gcrPrimePower g == False = c0
| n /= (fst $ head g) = c0
| otherwise = cPrimeTowerLevelI (snd $ head g) n (l+1)
cPrimeTowerLevelI _ _ _ = 0
cToGCR :: Canon -> GCR_
cToGCR c = case gAtt of
Just g -> g
_ -> error $ noConvError
where gAtt = cToGCR' c
noConvError = "Could not to convert unwieldy hyper expression to canonical rep: " ++ show c ++ "."
cToGCR' :: Canon -> Maybe GCR_
cToGCR' (Can x _) = Just x
cToGCR' (Bare x s) | s == Simp || gcrBigComposite (x, c1)
= Just $ if x == 1 then gcr1 else [(x, c1)]
| otherwise = cToGCR' (makeCanon x)
cToGCR' c | any (\c' -> cHyperExpr c' && (cHyOpLvlOutOfRange $ cGetHyperOp c')) $ cGetFactors c
= Nothing
| any (not . cSimplified) f
= Nothing
| otherwise = Just g
where f = cGetFactors $ cQuasiCanonize c
g = sortOn fst $ concat $ map conv f
conv (Bare n _) = [(n, c1)]
conv (Can g' _) = g'
conv (HX _ l _) = [(cToI (l !! 0), l !! 1)]
gcrNegateCanonical :: GCR_ -> CanonValueType -> Canon
gcrNegateCanonical g v | gcrNegative g = case gcrPrime (tail g) of
True -> Bare (fst $ head $ tail g) Simp
False -> Can (tail g) v
| otherwise = Can (gcreN1 : g) v
gcrNegate :: GCR_ -> GCR_
gcrNegate Pg0 = gcr0
gcrNegate x | gcrNegative x = tail x
| otherwise = gcreN1 : x
gcrNegative :: GCR_ -> Bool
gcrNegative PgNeg = True
gcrNegative _ = False
gcrPositive :: GCR_ -> Bool
gcrPositive PNeg = False
gcrPositive PZero = False
gcrPositive _ = True
gcrMult :: GCR_ -> GCR_ -> CycloMap -> (GCR_, CycloMap)
gcrMult x POne m = (x, m)
gcrMult POne y m = (y, m)
gcrMult _ Pg0 m = (gcr0, m)
gcrMult Pg0 _ m = (gcr0, m)
gcrMult x@(xh@(xp,xe):xs) y@(yh@(yp,ye):ys) m
= case compare xp yp of
LT -> (xh:g, m')
where (g, m') = gcrMult xs y m
EQ -> if gcrNegative x || expSum == c0
then gcrMult xs ys m
else ((xp, expSum):gf, mf)
where (expSum, m') = cAdd xe ye m
(gf, mf) = gcrMult xs ys m'
GT -> (yh:g, m')
where (g, m') = gcrMult x ys m
gcrMult x y _ = error e
where e = "Non-exhaustive pattern error in gcrMult. Params: " ++ show x ++ "*" ++ show y
gcr1, gcr0 :: GCR_
gcr1 = []
gcr0 = [(0, c1)]
gcreN1 :: GCRE_
gcreN1 = (-1, c1)
gcrToI :: GCR_ -> Integer
gcrToI g = product $ map f g
where f (p, e) | ce > 0 = p ^ ce
| otherwise = error negExpErr
where ce = cToI e
negExpErr = "gcrToI: Negative exponent found trying to convert " ++ show g
gcrToD :: GCR_ -> Double
gcrToD g = product $ map (\(p,e) -> (fromIntegral p) ** cToD e) g
gcrCmp :: GCR_ -> GCR_ -> Ordering
gcrCmp POne y = gcrCmpTo1 y True
gcrCmp x POne = gcrCmpTo1 x False
gcrCmp x y | x == y = EQ
| xN && yN = compare (gcrToC $ tail y) (gcrToC $ tail x)
| xN = LT
| yN = GT
| gcrIsZero x = LT
| gcrIsZero y = GT
| otherwise = case cSuperLogCmp (superLogCan x) (superLogCan y) of
EQ -> compare (gcrToI x) (gcrToI y)
cmp -> cmp
where (xN, yN) = (gcrNegative x, gcrNegative y)
gcrIsZero Pg0 = True;
gcrIsZero _ = False
gcrCmpTo1 :: GCR_ -> Bool -> Ordering
gcrCmpTo1 PNeg b = if b then GT else LT
gcrCmpTo1 Pg0 b = if b then GT else LT
gcrCmpTo1 _ b = if b then LT else GT
gcrGCD, gcrLCM :: GCR_ -> GCR_ -> GCR_
gcrGCD Pg0 y = y
gcrGCD x Pg0 = x
gcrGCD x y | crFactCutoff > 0 &&
((gcrIncompFact x && gcrUnsmooth y) ||
(gcrIncompFact y && gcrUnsmooth x)) = f spx spy
| otherwise = f x y
where f Pg1 _ = gcr1
f _ Pg1 = gcr1
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
gcrLCM Pg0 _ = gcr0
gcrLCM _ Pg0 = gcr0
gcrLCM x y | gcrNegative x || gcrNegative y = f (gcrAbs x) (gcrAbs y)
| crFactCutoff > 0 &&
((gcrIncompFact x && gcrUnsmooth y) ||
(gcrIncompFact y && gcrUnsmooth x)) = f spx spy
| otherwise = f x y
where f Pg1 y' = y'
f x' Pg1 = 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
spFactor :: GCR_ -> GCR_ -> (GCR_, GCR_)
spFactor x y = (sx ++ (grp $ sortOn fst $ px ++ spF cx (py ++ cy)),
sy ++ (grp $ sortOn fst $ py ++ spF cy (px ++ cx)) )
where spl n = (s, p, c)
where (s, r) = partition gcrSmooth n
(p, c) = partition (\ce -> not $ gcrBigComposite 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
else (b,e):g' (b', e') rs
where (b', e') = r
g' ce _ = [ce]
spF n (f:fs) = spF (concat $ map (proc f) n) fs
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
gcrUnsmooth, gcrIncompFact :: GCR_ -> Bool
gcrUnsmooth = any (\ce -> not $ gcrSmooth ce)
gcrIncompFact = any gcrBigComposite
gcrBigComposite, gcrSmooth :: GCRE_ -> Bool
gcrSmooth (b,_) = b <= crSmallFactCutoff
gcrBigComposite (b,_) = bigComposite b
bigComposite :: Integer -> Bool
bigComposite b = crFactCutoff > 0 && b > crFactCutoff && (not $ isPrime b)
divisionError :: String
divisionError = "gcrDiv: As requested per param, the dividend must be a multiple of the divisor."
divByZeroError :: String
divByZeroError = "gcrDiv: Division by zero error!"
zeroDivZeroError :: String
zeroDivZeroError = "gcrDiv: Zero divided by zero is undefined!"
gcrDivStrict :: GCR_ -> GCR_ -> GCR_
gcrDivStrict x y = case gcrDiv x y of
Left errorMsg -> error errorMsg
Right results -> results
gcrDiv :: GCR_ -> GCR_ -> Either String GCR_
gcrDiv Pg0 Pg0 = Left zeroDivZeroError
gcrDiv Pg0 _ = Right gcr0
gcrDiv _ Pg0 = Left divByZeroError
gcrDiv n d = g' n d
where g' x POne = Right x
g' POne _ = Left divisionError
g' x y
| gcrNegative y = g' (gcrNegate x) (gcrAbs y)
| otherwise = case compare xp yp of
LT -> case (g' xs y) of
Left _ -> Left divisionError
Right res -> Right ((xp, xe) : res)
EQ | xe > ye -> case (g' xs ys) of
Left _ -> Left divisionError
Right res -> Right ((xp, xe - ye) : res)
EQ | xe == ye -> gcrDiv xs ys
_ -> Left divisionError
where ((xp,xe):xs) = x
((yp,ye):ys) = y
gcrAbs :: GCR_ -> GCR_
gcrAbs x | gcrNegative x = tail x
| otherwise = x
gcrToCR :: GCR_ -> CR_
gcrToCR c = map (\(p,e) -> (p, cToI e)) c
gcrBare :: GCR_ -> Bool
gcrBare PBare = True
gcrBare POne = True
gcrBare _ = False
gcrPrime :: GCR_ -> Bool
gcrPrime PgPrime = True
gcrPrime _ = False
gcrPrimePower :: GCR_ -> Bool
gcrPrimePower PgPPower = True
gcrPrimePower _ = False
gcrOdd, gcrEven :: GCR_ -> Bool
gcrOdd Pg0 = False
gcrOdd POne = True
gcrOdd c | gcrNegative c = gcrOdd (gcrAbs c)
| otherwise = cp /= 2
where (cp,_):_ = c
gcrEven g = not (gcrOdd g)
gcrEqCheck :: GCR_ -> GCR_ -> Bool
gcrEqCheck POne POne = True
gcrEqCheck POne _ = False
gcrEqCheck _ POne = False
gcrEqCheck ((xp,xe):xs) ((yp,ye):ys) | xp /= yp || xe /= ye = False
| otherwise = gcrEqCheck xs ys
gcrEqCheck x y = error e
where e = "Non-exhaustive patterns in gcrEqCheck comparing " ++ show x ++ " to " ++ show y
gcrSimplified :: GCR_ -> Bool
gcrSimplified = all (\(b,e) -> cSimplified e && check b)
where check n = crFactCutoff <= 0 || n < crFactCutoff || (n > crFactCutoff && isPrime n)
cGetBase :: CanonElement -> Canon
cGetBase (b, _) = b
cGetExponent :: CanonElement -> Canon
cGetExponent (_, e) = e
cGetBases :: Canon -> [Canon]
cGetBases = cGetBases' False False False
cGetBases' :: Bool -> Bool -> Bool -> Canon -> [Canon]
cGetBases' f d h c
= if d then rMultiple else nub r'
where rMultiple = map fst $ filter (\(_,ct) -> ct /= 1) $ map (\l -> (head l, length l)) $ group $ sort r'
r' = map fst $ if f then (filter (\(_, f') -> f') r) else r
r = g' (abs c) h
g' b@(Bare _ _) h' = if h' || b == c1 then [] else [(b,True)]
g' (Can g _) h' = map (\c' -> (cGetBase $ convGCREToCE c', True)) g2
where g2 = if not h' then g else (filter (\(_,e) -> cHyperExprAny e) g)
g' c'@(HX PoA _ _) _ = [(c', not $ (f && (cHyOpLvlOutOfRange $ cGetHyperOp c')))]
g' (HX PoM cL _) h' = concat $ (g' (head cL) h'):(map (\e -> g' e False) (tail cL))
g' (HX y cL _) _ = map (\(c',_) -> (c',pF)) $ g' (head cL) False
where pF = not $ f && cHyOpLvlOutOfRange y
cGetBasesDeep :: Canon -> [Canon]
cGetBasesDeep c@(HX PoA l _) = sort $ nub ((c':i) ++ sF)
where i = foldl1 intersect $ map cGetBasesDeep l
iP = product i
c' = simpleHX cAddOpLevel (map (\a -> div a iP) l)
sF = filter (\p -> mod c p == 0) $ smallPrimeCanons
cGetBasesDeep c = cGetBases c
cGetExponents :: Canon -> [Canon]
cGetExponents (Bare _ _) = [c1]
cGetExponents (Can g _) = map (cGetExponent . convGCREToCE) g
cGetExponents (HX PoA _ _) = [c1]
cGetExponents hx@(HX _ _ _) = map (cGetExponent . convGCREToCE) $ cToGCR hx
cGetElements:: Canon -> [CanonElement]
cGetElements b@(Bare _ _) = [(b, c1)]
cGetElements (Can g _) = map convGCREToCE g
cGetElements hx@(HX _ _ _) = map convGCREToCE $ cToGCR hx
convGCREToCE :: GCRE_ -> CanonElement
convGCREToCE (b, e) = (makeCanon b, e)
cNumDivisors, cTau :: Canon -> Canon
cNumDivisors c = case (cNumDivisors' c) of
Left s -> error s
Right v -> v
cTau = cNumDivisors
cNumDivisors' :: Canon -> Either String Canon
cNumDivisors' c
| not (canComputeDivs c') = Left $ "Canon was zero, not integral or not completely factored. Can't compute."
| otherwise = case cToGCR' c' of
Just g -> Right $ product $ map (\(_,e) -> 1 + e) g
Nothing -> Left "Unknown issue converting to underlying GCR structure."
where c' = abs c
cNthDivisor :: Canon -> Canon -> Either String Canon
cNthDivisor _ (Bare _ NSim) = Left "cNthDivisor: Bare integer has not been simplified."
cNthDivisor n c | cNegative n || not (cIntegral n)
= Left "cNthDivisor: Both n must be integral and >= 0"
| not (canComputeDivs aC) = Left "cNthDivisor: Canon was zero, not integral or not completely factored. Can't compute"
| cHyperExpr c = nthHyper $ cToGCR aC
| otherwise = nth aC
where aC = abs c
nth Pc0 = Right n
nth cn = case f (cAbs n) (cToGCR cn) of
Right r -> Right $ gcrToC r
Left e -> Left e
where f Pc0 _ = Right gcr1
f _ Pg1 = Left "cNthDivisor: Bad dividend number requested."
f n' c' = case f (div n' (e + 1)) cs of
Right r -> Right $ if m == c0 then r else ((b,m):r)
e' -> e'
where (b,e):cs = c'
m = mod n' (e + 1)
nthHyper :: GCR_ -> Either String Canon
nthHyper x | n >= nd = Left "cNthDivisor: Bad dividend number requested of hyper expression."
| otherwise = Right $ compute n divL c1
where nd = product $ map snd divL
divL = map (\(p,e) -> (Bare p Simp, e + 1)) x
compute n' ((p,d):ds) wP | nq == 0 = newWp
| otherwise = compute nq ds newWp
where (nq, m) | n' < d = (c0, n')
| cHyperExpr d && n' < 100 * d
= proc n' (0 :: Int)
| otherwise = (quotRem n' d)
newWp = if m == c0 then wP else (wP * p <^ m)
proc n2 rc | n2 < d = (makeCanon $ toInteger rc, n2)
| otherwise = proc (n2 - d) (rc + 1)
compute _ _ wP = wP
cWhichDivisor :: Canon -> Canon -> Either String Canon
cWhichDivisor d c | not (cIntegral d) = Left "cWhichDivisor: d must be integral"
| d == c0 = Left "cWhichDivisor: Zero is not a proper divisor"
| d == c1 && aC /= c0 = Right c0
| not (canComputeDivs aC) = Left "cWhichDivisor: Canon was either zero or not completely factored. Can't compute"
| d == c = Right $ cNumDivisors c - 1
| otherwise = case gcrDiv gAc gAd of
Right _ -> Right $ wD gAd divLProg c0
Left _ -> Left "cWhichDivisor: d is not a divisor of c"
where (aD, aC, gAd, gAc) = (abs d, abs c, cToGCR aD, cToGCR aC)
divLProg = zip (map fst gAc) (c1:(init $ scanl1 (*) $ map (\(_,e) -> e + 1) gAc))
wD a@((aP,aE):as) ((bP,bC):bs) wS | aP < bP = error "Logic error: Unexpected factors found in divisor"
| aP > bP = wD a bs wS
| otherwise = wD as bs (wS + aE * bC)
wD a _ wS | null a = wS
| otherwise = error "Logic error: Unexpected factors found in divisor"
cDivisors :: Canon -> Either String [Canon]
cDivisors c | not (canComputeDivs c') = Left "cWhichDivisor: Canon was either zero or not completely factored. Can't compute"
| otherwise = divs c'
where c' = abs c
divs (Bare x _) | x == 1 = Right [c1]
| otherwise = Right [c1, makeCanon x]
divs (Can g _) = Right $ map gcrToC $ foldr1 cartProd $ map pwrDivList g
where cartProd xs ys = [x ++ y | y <- ys, x <- xs]
pwrDivList (n,e) = [if y == 0 then gcr1
else [(n, makeCanon y)] | y <- [0 .. cToI e]]
divs _ = error "cDivisors can't return all of the divisors for hyper expressions!"
cGetFirstNDivisors :: Int -> Canon -> Either String [Canon]
cGetFirstNDivisors n c@(HX _ _ IntC)
| any errPred divList = Left "cGetNDivisors: Canon was either zero or not completely factored. Can't compute"
| otherwise = Right $ map clean divList
where divList = map (\i -> cNthDivisor (makeCanon $ toInteger i) c) [0..n-1]
errPred (Left _) = True
errPred (Right _) = False
clean (Left _) = error "'Dirty list' passed to clean function"
clean (Right v) = v
cGetFirstNDivisors n c = case cDivisors c of
Right ds -> Right $ take n ds
msg -> msg
canComputeDivs :: Canon -> Bool
canComputeDivs c | cBare c && (cToI c == 0) = False
| not (cSimplified c && cIntegral c) = False
| not (cHyperExpr c) = True
| cHyperSum c = False
| cMaxHyperOpForQC c > maxHyperOpDelveLevel = False
| cHyperProd c && not (all canComputeDivs cL) = False
| otherwise = canComputeDivs b
where cL@(b:_) = cGetHyperList c
smallPrimeCanons :: [Canon]
smallPrimeCanons = map (\p -> Bare p Simp) $ map unPrime $ take 1000 primes
cRelativelyPrime :: Canon -> Canon -> Maybe Bool
cRelativelyPrime x y | x == c1 || y == c1 = Just True
| cEven x && cEven y = Just False
| not (null iBs) = Just False
| xHs == c1 && yHs == c1 = Just True
| xHs /= c1 && f xHs yB = Just False
| yHs /= c1 && f yHs xB = Just False
| xHs /= c1 && yHs /= c1 = if hpi then (Just False) else Nothing
| otherwise = Nothing
where (xB, yB) = (cGetBases x, cGetBases y)
iBs = intersect xB yB
hs b = product $ filter cHyperSum $ b
(xHs, yHs) = (hs xB, hs yB)
f x' l = any (\p -> mod x' p == 0) l
hpi = any (\p -> mod x p == 0 && mod y p == 0) $ smallPrimeCanons
type SuperPairC = (Canon, Double)
type SuperPairI = (Integer, Double)
infinity :: Double
infinity = read "Infinity"
integerDblCutoff, manualLB :: Integer
integerDblCutoff = 2 ^ ((snd $ floatRange (1.0 :: Double)) - 1)
manualLB = lBI ^ (16 :: Int)
dblLogMantissaUB :: Double
dblLogMantissaUB = logD $ fromInteger integerDblCutoff
superLogICutoff :: SuperPairI
superLogICutoff = (2, 5.0)
cSuperLogCutoff :: SuperPairC
cSuperLogCutoff = (makeCanon $ fst superLogICutoff, snd superLogICutoff)
cSuperLog :: Canon -> (SuperPairC, Int)
cSuperLog (Bare n _) = (makeSuperLogC $ superLogI n, if n > 0 then 1 else (-1))
cSuperLog c@(Can g _) = if d == c1 then (superLogCan g, s)
else (slDiv (superLogCan $ cToGCR n) (superLogCan $ cToGCR d), s)
where (n, d) = cSplit c
s = if cPositive c then 1 else (-1)
cSuperLog (HX PoA s _) = cSuperLogSum s
cSuperLog (HX PoM p _) = cSuperLogProd p
cSuperLog (HX PoE e _) = (cSuperLogExp e, 1)
cSuperLog c@(HX h (b:cs) _) | cMaxHyperOpForQC c > maxHyperOpDelveLevel
= error $ "Unable to take cSuperLog of massive hyper expression: " ++ show c
| h == cTetrOpLevel = ((sv1 + offset, m), 1)
| otherwise = ((c1 + offset + (head $ tail $ cGetHyperList $ cNestExpTail c False), m), 1)
where (offset, m) = getTowerMantissa b sv1
sv1 = cApplyHy h cs True
cSuperLog _ = error "Logic error in Super Log: Default Canon configuration unexpectedly reached"
superLogCan :: GCR_ -> SuperPairC
superLogCan g = fst $ slProd $ map f g
where f (b, e) = if e == 1
then cSuperLog (bFmt b)
else (cSuperLogExp ((bFmt b):[e]), if b > 0 then 1 else (-1))
bFmt b = (Bare b Simp)
towerHeight :: Integer
towerHeight = 8
towerHeightC :: Canon
towerHeightC = makeCanon towerHeight
getTowerMantissa :: Canon -> Canon -> (Canon, Double)
getTowerMantissa b h = (lvl - makeCanon htu, m)
where (lvl, m) = slExp $ replicate (fromInteger htu) slb
slb = fst $ cSuperLog b
htu = if h < towerHeightC then (cToI h) else towerHeight
integerShowCutoff :: Integer
integerShowCutoff = (10 :: Integer) ^ (1000 :: Integer)
superLogI :: Integer -> SuperPairI
superLogI i | i > 0 = suL i
| i < 0 = suL $ abs i
| otherwise = (0, 0.0)
where suL n | n > integerDblCutoff = refine $ spLog' (1, mC 0 n)
| otherwise = refine $ spLog' (0, iToD)
where spLog' (lvl', n')
= if ln > lB then (spLog' (lvl' + 1, ln)) else (lvl', n')
where ln = logB n'
iToD | d == infinity = error "Raise bug: Number below cutoff still returning infinity."
| otherwise = d
d = fromIntegral n
mC :: Integer -> Integer -> Double
mC c n' | n' <= manualLB = fromIntegral c + (logB $ fromIntegral n')
| otherwise = mC (c+1) (div n' lBI)
refine (l,v) = if v > lB
then refine (l+1, logB v)
else (if (v < 1.0 || (v == 1 && l > 0))
then (l-1, powB v) else (l, v))
makeSuperLogC :: SuperPairI -> SuperPairC
makeSuperLogC (spi, d) = (makeCanon spi, d)
cSuperLogProd, cSuperLogSum :: [Canon] -> (SuperPairC, Int)
cSuperLogProd cL = slProd $ map cSuperLog cL
cSuperLogSum cL = slSum $ map cSuperLog cL
slEpsilon :: Double
slEpsilon = 2.0 * 10.0 ** (-15.0)
slProd, slSum :: [(SuperPairC, Int)] -> (SuperPairC, Int)
slProd sL | null sL = error "Null list passed to slProd"
| otherwise = foldl1 (\(s, b) (ws, wb) -> (slMult s ws, b * wb)) sL
slSum l | length posCL == 0 = (negSL, -1)
| length negCL == 0 = (posSL, 1)
| pl == nl && pm == nm = ((c0, 0.0), 0)
| (pl == nl && pm > nm) || pl > nl = (cmpSl posSL negSL, 1)
| otherwise = (cmpSl negSL posSL, -1)
where (posCL, negCL) = partition (\(_, s) -> s == 1) l
posSL = foldl1 slAdd $ map fst posCL
negSL = foldl1 slAdd $ map fst negCL
(pl,pm) = posSL
(nl,nm) = negSL
cmpSl (l1, m1) (l2, m2) = case (l1 - l2) of
0 -> case l1 of
0 -> (l1, m1-m2)
1 -> slRefine (l1, logB $ powB m1 - powB m2)
_ | l1 == c2 && m1 < dblLogMantissaUB ->
slRefine (l1, logD $ powD m1 - powD m2)
_ | otherwise ->
(l1, m1)
1 -> case l1 of
1 -> slRefine (l1, logB $ powB m1 - m2)
_ | l1 == c2 && m1 < dblLogMantissaUB ->
slRefine (l1, logD $ powD m1 - powB m2)
_ | otherwise ->
(l1, m1)
_ | l1 == c2 && m1 < dblLogMantissaUB ->
slRefine (l1, logD $ powD m1 - m2)
_ | otherwise ->
(l1, m1)
slAdd, slMult, slDiv :: SuperPairC -> SuperPairC -> SuperPairC
slAdd e1 e2 = if cSuperLogGT e1 e2 then (add2 e1 e2) else (add2 e2 e1)
where add2 (l1, m1) (l2, m2) = case l1 - l2 of
0 -> case l1 of
0 -> slRefine (l1, m1 + m2)
1 -> slRefine (l1, logB $ powB m1 + powB m2)
_ | l1 == c2 && m1 < dblLogMantissaUB && tot < integerDblCutoff ->
slRefine (l1, logD $ m1pd + m2pd)
where (m1pd, m2pd) = (powD m1, powD m2)
tot = round m1pd + round m2pd + 1
_ | otherwise ->
(l1, m1)
1 -> case l1 of
1 -> slRefine (l1, logB $ powB m1 + m2)
_ | l1 == c2 && m1 < dblLogMantissaUB ->
slRefine (l1, logD $ powD m1 + powB m2)
_ | otherwise ->
(l1, m1)
_ | l1 == c2 && m1 < dblLogMantissaUB ->
slRefine (l1, logD $ powD m1 + powB m2)
_ | otherwise ->
(l1, m1)
slMult e1 e2 = if cSuperLogGT e1 e2 then (mul2 e1 e2) else (mul2 e2 e1)
where mul2 (l1, m1) (l2, m2) = case l1 - l2 of
0 -> case l1 of
0 -> slRefine (l1, m1 * m2)
1 -> slRefine (l1, m1 + m2)
2 -> slRefine (l1, logB $ powB m1 + powB m2)
_ | l1 == c3 && m1 < dblLogMantissaUB && tot < integerDblCutoff ->
slRefine (l1, logD $ m1pd + m2pd)
where (m1pd, m2pd) = (powD m1, powD m2)
tot = round m1pd + round m2pd + 1
_ | otherwise ->
(l1, m1)
1 -> case l1 of
1 -> slRefine (l1, m1 + logB m2)
2 -> slRefine (l1, logB $ powB m1 + m2)
_ | l1 == c3 && m1 < dblLogMantissaUB ->
slRefine (l1, logD $ powD m1 + powB m2)
_ | otherwise ->
(l1, m1)
2 -> case l1 of
2 -> slRefine (l1, logB $ powB m1 + logB m2)
_ | l1 == c3 && m1 < dblLogMantissaUB ->
slRefine (l1, logD $ powD m1 + m2)
_ | otherwise ->
(l1, m1)
_ | l1 == c3 && m1 < dblLogMantissaUB ->
slRefine (l1, logD $ powD m1 + logB m2)
_ | otherwise ->
(l1, m1)
slDiv a@(l1', m1') b@(l2', m2')
| l1' < 0 && l2' < 0 = slDiv (slInvert b) (slInvert a)
| l2' < 0 = slMult a (slInvert b)
| l1' > l2' || (l1' == l2' && m1' > m2')
= slRefine $ slDiv' a b
| otherwise = slInvert $ slRefine $ slDiv b a
where slInvert (l, m) = (negate l, m)
slDiv' (l1, m1) (l2, m2)
| l1 == 0 = (c0, m1 / m2)
| l1 == 1 = case l2 of
0 -> (l1, m1 - logB m2)
_ -> (l1, m1 - m2)
| l1 == 2 = case l2 of
0 -> (l1, logB (powB m1 - logB m2))
1 -> (l1, logB (powB m1 - m2))
_ -> (l1, logB (powB m1 - powB m2))
| l1 == 3 && m1 < dblLogMantissaUB
= case l2 of
0 -> (l1, logD (powD m1 - logB m2))
1 -> (l1, logD (powD m1 - m2))
2 -> (l1, logD (powD m1 - powB m2))
_ -> (l1, logD (powD m1 - powD m2))
| otherwise = (l1, m1)
cSuperLogExp :: [Canon] -> SuperPairC
cSuperLogExp cL = slExp $ map (fst . cSuperLog) cL
slExp :: [SuperPairC] -> SuperPairC
slExp (x:r@(_:_)) = combineS x (slExp r)
where combineS (nl, nm) (rl, rm)
= case rl of
0 -> case nl of
0 -> slRefine (nl, nm ** rm)
1 -> slRefine (nl, nm * rm)
2 -> slRefine (nl, nm + logB rm)
3 -> slRefine (nl, logB $ powB nm + logB rm)
_ | nl == 4 && nm < dblLogMantissaUB
-> slRefine (nl, logD $ powD nm + logB rm)
| otherwise
-> (nl, nm)
1 -> case nl of
0 -> slRefine (rl, logB nm * powB rm)
1 -> slRefine (rl, nm * powB rm)
2 -> slRefine (rl, powB nm * powB rm)
3 -> slRefine (nl, logB $ powB nm + rm)
_ | nl == 4 && nm < dblLogMantissaUB
-> slRefine (nl,logD $ powD nm + rm)
| otherwise
-> (nl, nm)
2 -> case nl of
0 -> slRefine (rl, logD nm + powB rm)
1 -> slRefine (rl, logB nm + powB rm)
2 -> slRefine (rl, nm + powB rm)
3 -> slRefine (rl, powB nm + powB rm)
_ | nl == 4 && nm < dblLogMantissaUB ->
slRefine (rl, powD nm + powB rm)
| otherwise ->
(nl, nm)
_ | rl == 3 && rm < dblLogMantissaUB ->
case nl of
0 -> slRefine (rl, logB $ logD nm + powD rm)
1 -> slRefine (rl, logB $ logB nm + powD rm)
2 -> slRefine (rl, logB $ nm + powD rm)
3 -> slRefine (rl, logB $ powB nm + powD rm)
4 -> if nm < dblLogMantissaUB && tot < integerDblCutoff
then slRefine (rl, logB $ nmpd + rmpd)
else (nl, max nm rm)
where (nmpd, rmpd) = (powD nm, powD rm)
tot = round nmpd + round rmpd + 1
_ -> (nl, nm)
| otherwise ->
case compare nl (rl + c1) of
EQ -> (nl, max nm rm)
GT -> (nl, nm)
LT -> (rl + c1, rm)
slExp (x:_) = slRefine x
slExp _ = (0, 1.0)
slRefine :: SuperPairC -> SuperPairC
slRefine (lvl, v) = if v > lB
then slRefine (lvl + c1, logB v)
else (if v <= 1.0 then (lvl - c1, powB v) else (lvl, v))
cSuperLogCmp :: SuperPairC -> SuperPairC -> Ordering
cSuperLogCmp (l1, m1) (l2, m2) | l1 > l2 = GT
| l1 < l2 = LT
| abs (m1 - m2) < slEpsilon = EQ
| otherwise = if l1 >= 0 then (compare m1 m2) else (compare m2 m1)
cSuperLogGT :: SuperPairC -> SuperPairC -> Bool
cSuperLogGT x y = case cSuperLogCmp x y of
GT -> True
_ -> False
lBI :: Integer
lBI = 10
lB :: Double
lB = fromIntegral lBI
logB, logD, powB, powD :: Double -> Double
logB a = log a / log lB
logD a = logB $ logB a
powB a = lB ** a
powD a = lB ** (lB ** a)
instance CanonConv Canon where
toSC c = toSC $ cToCR c
toRC c = toRC $ cToCR c
pattern PBare :: forall t. [(t, Canon)]
pattern PBare <- [(_, Bare 1 _)]
pattern PgPPower :: forall t a. (Num a, Ord a) => [(a, t)]
pattern PgPPower <- [(compare 1 -> LT, _ )]
pattern PgPrime :: forall a. (Num a, Ord a) => [(a, Canon)]
pattern PgPrime <- [(compare 1 -> LT, Bare 1 _)]
pattern PgNeg :: forall a. (Num a, Eq a) => [(a, Canon)]
pattern PgNeg <- ((-1, Bare 1 _):_)
pattern Pg0 :: forall a. (Num a, Eq a) => [(a, Canon)]
pattern Pg0 <- [(0, Bare 1 _)]
pattern Pg1 :: forall t. [t]
pattern Pg1 = []
pattern Pc0 :: Canon
pattern Pc0 <- Bare 0 _
pattern Pc1 :: Canon
pattern Pc1 <- Bare 1 _
pattern PoA :: Canon
pattern PoA <- Pc1
pattern Pc2 :: Canon
pattern Pc2 <- Bare 2 _
pattern PoM :: Canon
pattern PoM <- Pc2
pattern Pc3 :: Canon
pattern Pc3 <- Bare 3_
pattern PoE :: Canon
pattern PoE <- Pc3
pattern PcN1 :: Canon
pattern PcN1 <- Can [(-1, Bare 1 _)] _
cMaxExpoToExpand :: Canon
cMaxExpoToExpand = c4
distHyperExpr, distSum, distProduct, distExpo :: Canon -> Canon -> ([Canon], Bool)
distHyperExpr c m | h == cAddOpLevel = (dS, dS /= hL)
| h == cMultOpLevel && fP = (dP, fP)
| h == cExpOpLevel && fE = (dE, fE)
| otherwise = ([c], False)
where (h, hL) = (cGetHyperOp c, cGetHyperList c)
((dS,_), (dP,fP), (dE,fE)) = (distSum c m, distProduct c m, distExpo c m)
distSum (HX PoA l _) m = (concat dM, any id fM)
where (dM, fM) = unzip $ map (\x -> distHyperExpr x m) l
distSum c _ = ([c], False)
distProduct c@(HX PoM l _) m | not (null sums) = (dist (head sums') (tail sums') [computeExpr cMultOpLevel nonSums], True)
| otherwise = ([c], False)
where sums' = map exP sums
exP s = if polyPred s m then simpleHX c1 (fst $ distExpo s m) else s
(sums, nonSums) = partition (\e -> sumPred e || polyPred e m) l
distProduct c _ = ([c], False)
dist :: Canon -> [Canon] -> [Canon] -> [Canon]
dist x y wL | length y > 0 = dist (head y) (tail y) cartProd
| otherwise = cartProd
where cartProd = if null wL then hLx else (concat $ map (\a -> map (* a) wL) hLx)
hLx = cGetHyperList x
distExpo c m | polyPred c m = cExpandPoly (cGetHyperList $ head cL) (fromInteger $ cToI (cL !! 1)) m
| otherwise = ([c], False)
where cL = cGetHyperList c
sumPred :: Canon -> Bool
sumPred c = cGetHyperOp c == cAddOpLevel
polyPred :: Canon -> Canon -> Bool
polyPred (HX h (b:e:r) _) m = h == cExpOpLevel && sumPred b && e <= m && null r
polyPred _ _ = False
cExpandPoly :: [Canon] -> Int -> Canon -> ([Canon], Bool)
cExpandPoly (a:r@(_:_:_)) e m = (eP, True)
where eP = concat $ map (\x -> fst $ distHyperExpr x m) $
concat $ map (\x -> fst $ distHyperExpr x m) $
fst $ cExpandPoly [a, simpleHX cAddOpLevel r] e m
cExpandPoly (a:b:_) e _ = (map (\i -> product [makeCanon $ binomial e i, raise a i, raise b (e-i)]) [0..e], True)
where raise b' e' | e' == 0 = c1
| e' == 1 = b'
| otherwise = expH b' (makeCanon $ toInteger e')
cExpandPoly c _ _ = (c, False)
factorial :: [Integer]
factorial = (1 :: Integer) : 1 : zipWith (*) [2..] (tail factorial)
binomial :: Int -> Int -> Integer
binomial n m | n < 0 = error "Binomial: n must be >= 0"
| m < 0 || m > n = error "Binomial: m must be >= 0 and <= n"
| otherwise = div (factorial !! n) (factorial !! m * factorial !! (n-m))
computeExpr :: Canon -> [Canon] -> Canon
computeExpr hy l
| null nL = dV
| length nL == 1 = head nL
| otherwise = simpleHX hy nL
where
nL | hy == cAddOpLevel = filter (/= dV) (hE ++ [f nHe])
| hy == cMultOpLevel = filter (/= dV) ((f nHe):hE)
| otherwise = l
(hE, nHe) = partition cHyperExprAny l
(dV, f) | hy == cAddOpLevel = (c0, sum)
| otherwise = (c1, product)
simplifySum, simplifyProd :: [Canon] -> Canon
simplifySum l = checkToFlipSum $ combineSum $ computeExpr cAddOpLevel l
simplifyProd l = if (any (== 0) l) then c0 else (combineProd $ computeExpr cMultOpLevel l)
checkToFlipSum :: Canon -> Canon
checkToFlipSum r = if cGetHyperOp r == cAddOpLevel && cNegative r
then simpleHX cMultOpLevel [cN1, negate r]
else r
addH, multH, expH, addH', multH', expH' :: Canon -> Canon -> Canon
(addH, multH, expH) = (prep addH', prep multH', prep expH')
prep :: (Canon -> Canon -> Canon) -> Canon -> Canon -> Canon
prep f a b | (cHyperExprAny a || cHyperExprAny b) && (not (cIntegral a) || not (cIntegral b))
= error "Can't have sums or products with non-integers and hyper expressions"
| otherwise = f a b
addH' (HX PoA l1 _) (HX PoA l2 _) = simplifySum $ l1 ++ l2
addH' (HX PoA lA _) b | cHyperSum b = simplifySum $ lA ++ (negSumList b)
| otherwise = simplifySum (b:lA)
addH' b a@(HX PoA _ _) = addH' a b
addH' a@(HX PoM _ _) b@(HX PoM _ _) | aHs && bHs = simplifySum $ aNs ++ bNs
| aHs = simplifySum (b:aNs)
| bHs = simplifySum (a:bNs)
| otherwise = simplifySum [a,b]
where (aHs, bHs) = (cHyperSum a, cHyperSum b)
(aNs, bNs) = (negSumList a, negSumList b)
addH' a@(HX PoM _ _) b | cHyperSum a = simplifySum (b:negSumList a)
| otherwise = simplifySum [a,b]
addH' b a@(HX PoM _ _) = addH' a b
addH' a b | cHyperExprAny a || cHyperExprAny b = simplifySum [a,b]
| otherwise = a + b
multH' a b | nhs a && nhs b = simplifyProd [negate a, negate b]
| cHyperExpr a || cHyperExpr b = if snp == cN1 then negate hp else hp
| otherwise = fst $ cMult a b crCycloInitMap
where nhs x = cHyperSum x && cGetHyperOp x == cMultOpLevel
snp = signum a * signum b
hp = simplifyProd $ cGetFactors (abs a) ++ cGetFactors (abs b)
expH' a b | cHyperExprAny a || cHyperExprAny b = fst $ cHyperOp cExpOpLevel [a,b] crCycloInitMap
| otherwise = fst $ cExp a b False crCycloInitMap
negSumList :: Canon -> [Canon]
negSumList c = map negate $ cGetHyperList ((cGetHyperList c) !! 1)
cConvertToSum :: Canon -> (Canon, Bool)
cConvertToSum x = cConvertToSum' x cMaxExpoToExpand
cConvertToSum' :: Canon -> Canon -> (Canon, Bool)
cConvertToSum' x m | b = (checkToFlipSum $ computeExpr cAddOpLevel (sortByHpo $ map f d), b)
| otherwise = (x, b)
where f c = if cNegative c then (negate f') else f'
where f' = product $ cFlattenAndGroup' (abs c) m
(d,b) = distHyperExpr x m
simpleFactorPair :: Canon -> Canon -> Bool -> (Canon, Canon)
simpleFactorPair n d hF = (applyFcnForHy v cMultOpLevel cFlattenAndGroup, gcd')
where (gcd', x', y') = simpleReduce n d hF
v = computeExpr cMultOpLevel [gcd',x'+y']
simpleReduce :: Canon -> Canon -> Bool -> (Canon, Canon, Canon)
simpleReduce n d hF = (gcd', x', y')
where proc xL@(x@(xP, xE):xs) yL wL wX wY
| null yL = (wL, (wX ++ xL), wY)
| null z = proc xs yL wL (wX ++ [x]) wY
| length z > 1 = error "Entry occured was not unique in y!"
| xE == yE = proc xs (yL \\ z) (wL ++ [(xP,xE)]) wX wY
| xE < yE = proc xs (yL \\ z) (wL ++ [(xP,xE)]) wX (wY ++ [(xP,yE -xE)])
| otherwise = proc xs (yL \\ z) (wL ++ [(xP,yE)]) (wX ++ [(xP,xE - yE)]) wY
where z = filter (\(p,_) -> xP == p) yL
yE = snd $ head z
proc _ yL wL wX wY = (wL, wX, (wY ++ yL))
((nN, nH'), (dN, dH')) = (simplePrep n, simplePrep d)
nHq = if hF then 1 else gcd nN dN
dem (a, b, c) = (map expDemote a, map expDemote b, map expDemote c)
(wL', wX', wY') = dem $ proc nH' dH' [] [] []
gcd' = computeExpr cMultOpLevel (nHq :wL')
x' = computeExpr cMultOpLevel ((nN/nHq):wX')
y' = computeExpr cMultOpLevel ((dN/nHq):wY')
simplePrep :: Canon -> (Canon, [(Canon, Canon)])
simplePrep c = (product nN, map expPromote nH)
where (nH, nN) = partition cHyperExpr $ cGetFactors c
expPromote :: Canon -> (Canon, Canon)
expPromote v | cGetHyperOp v == cExpOpLevel = (head h, computeExpr cExpOpLevel (tail h))
| otherwise = (v, c1)
where h = cGetHyperList v
expPromoteFull :: Canon -> Canon
expPromoteFull c | cMaxHyperOpForQC c > maxHyperOpDelveLevel
= error "expPromoteFull: Can't perform this action. Max hyper op at base level exceeded."
| otherwise = simpleHX cMultOpLevel newFactors
where (hE, nonHe) = partition cHyperExpr $ cGetFactors $ cQuasiCanonize c
prmNonHe = map (\(p,e) -> (makeCanon p, e)) $ concat $ map cToGCR nonHe
newFactors :: [Canon]
newFactors = map (\(p,e) -> simpleHX cExpOpLevel [p,e]) $
sortOn fst $ prmNonHe ++ (concat $ map (\c' -> map expPromote $ cGetFactors c') hE)
expDemote :: (Canon, Canon) -> Canon
expDemote (p, e) = if e == c1 then p else computeExpr cExpOpLevel [p,e]
cFactorSum :: Canon -> Bool -> Canon
cFactorSum c@(HX PoA hL _) hF | gcdL == c1 || (hF && not (cHyperExpr gcdL))
= c
| otherwise = computeExpr cMultOpLevel ((cGetFactors gcdL) ++ [computeExpr cAddOpLevel (map (\a -> div a gcdL) hL)])
where gcdL = foldl1 cGCD hL
cFactorSum c _ = c
cFactorHorizon :: Canon -> Canon
cFactorHorizon c | gcdL == c1 || length hL' == 1 = c
| otherwise = computeExpr cMultOpLevel ((cGetFactors gcdL) ++ [simpleHX cAddOpLevel aL])
where gcdL = foldl1 cGCD hL'
hL' = map expPromoteFull $ cGetAddends $ fst $ cConvertToSum c
mL = map (\a -> cCleanup $ div a gcdL) hL'
(b,nB) = partition cBare mL
sB = sum b
aL = if sB == 0 then nB else (nB ++ [sB])