module Math.Projects.KnotTheory.LaurentMPoly where
import qualified Data.Map as M
import Data.List as L
import Math.Algebra.Field.Base
newtype LaurentMonomial = LM (M.Map String Q) deriving (Eq)
degLM (LM m) = sum $ M.elems m
instance Ord LaurentMonomial where
compare a b = let ds = M.elems m where LM m = a/b in
case compare (sum ds) 0 of
GT -> GT
LT -> LT
EQ -> if null ds then EQ else
if head ds > 0 then GT else LT
instance Show LaurentMonomial where
show (LM a) | M.null a = "1"
| otherwise = concatMap showVar $ M.toList a
where showVar (v,1) = v
showVar (v,i) = v ++ "^" ++ show i
instance Num LaurentMonomial where
LM a * LM b = LM $ M.filter (/=0) $ M.unionWith (+) a b
fromInteger 1 = LM M.empty
instance Fractional (LaurentMonomial) where
recip (LM m) = LM $ M.map negate m
denominatorLM (LM a) = recip $ LM $ M.filter (<0) a
lcmLM (LM a) (LM b) = LM $ M.unionWith max a b
divLM a b = let LM c = a/b in if all (>=0) (M.elems c) then Just (LM c) else Nothing
newtype LaurentMPoly r = LP [(LaurentMonomial,r)] deriving (Eq,Ord)
instance Show r => Show (LaurentMPoly r) where
show (LP []) = "0"
show (LP ts) =
let (c:cs) = concatMap showTerm (reverse ts)
in if c == '+' then cs else c:cs
where showTerm (m,c) =
case show c of
"1" -> "+" ++ show m
"-1" -> "-" ++ show m
cs -> showCoeff cs ++ (if m == 1 then "" else show m)
showCoeff (c:cs) = if any (`elem` ['+','-']) cs
then "+(" ++ c:cs ++ ")"
else if c == '-' then c:cs else '+':c:cs
instance (Eq r, Num r) => Num (LaurentMPoly r) where
LP ts + LP us = LP (mergeTerms ts us)
negate (LP ts) = LP $ map (\(m,c)->(m,-c)) ts
LP ts * LP us = LP $ collect $ sortBy cmpTerm $ [(g*h,c*d) | (g,c) <- ts, (h,d) <- us]
fromInteger 0 = LP []
fromInteger n = LP [(fromInteger 1, fromInteger n)]
cmpTerm (a,c) (b,d) = case compare a b of EQ -> EQ; GT -> LT; LT -> GT
mergeTerms (t@(g,c):ts) (u@(h,d):us) =
case cmpTerm t u of
LT -> t : mergeTerms ts (u:us)
GT -> u : mergeTerms (t:ts) us
EQ -> if e == 0 then mergeTerms ts us else (g,e) : mergeTerms ts us
where e = c + d
mergeTerms ts us = ts ++ us
collect (t1@(g,c):t2@(h,d):ts)
| g == h = collect $ (g,c+d):ts
| c == 0 = collect $ t2:ts
| otherwise = t1 : collect (t2:ts)
collect ts = ts
instance (Eq r, Fractional r) => Fractional (LaurentMPoly r) where
recip (LP [(m,c)]) = LP [(recip m, recip c)]
recip _ = error "LaurentMPoly.recip: only supported for (non-zero) constants or monomials"
lm (LP ((m,c):ts)) = m
lc (LP ((m,c):ts)) = c
lt (LP ((m,c):ts)) = LP [(m,c)]
quotRemLP f g
| g == 0 = error "quotRemLP: division by zero"
| denominatorLP f /= 1 || denominatorLP g /= 1 = error "quotRemLP: negative exponents"
| otherwise = quotRemLP' f (0,0)
where
quotRemLP' 0 (q,r) = (q,r)
quotRemLP' h (q,r) =
case lm h `divLM` lm g of
Just m -> let t = LP [(m, lc h / lc g)]
in quotRemLP' (h-t*g) (q+t,r)
Nothing -> let lth = lt h
in quotRemLP' (h-lth) (q, r+lth)
reduceLP f g@(LP [_,_]) =
let fn = f * fd
fd = denominatorLP f
(_,rn) = quotRemLP fn g
(_,rd) = quotRemLP fd g
in rn / rd
var v = LP [(LM $ M.singleton v 1, 1)]
t = var "t" :: LaurentMPoly Q
x = var "x" :: LaurentMPoly Q
y = var "y" :: LaurentMPoly Q
z = var "z" :: LaurentMPoly Q
denominatorLP (LP ts) = LP [(m',1)] where
m' = foldl lcmLM 1 [denominatorLM m | (m,c) <- ts]
inject 0 = LP []
inject c = LP [(fromInteger 1, c)]
sqrtvar v = LP [(LM $ M.singleton v (1/2), 1)]
subst vts (LP us) = sum [inject c * substM m | (m,c) <- us] where
substM (LM m) = product [substV v ^^^ i | (v,i) <- M.toList m]
substV v =
let v' = var v in
case L.lookup v' vts of
Just t -> t
Nothing -> v'
f ^^^ i | denominatorQ i == 1 = f ^^ numeratorQ i
| otherwise = case f of
LP [(LM m,1)] -> LP [(LM $ M.map (*i) m ,1)]
otherwise -> error ("(^^^): Cannot calculate " ++ show f ++ " ^^^ " ++ show i)