hspray 0.2.3.0 → 0.2.4.0
raw patch · 6 files changed
+452/−246 lines, 6 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Math.Algebra.Hspray: gcdQX :: Spray Rational -> Spray Rational -> Spray Rational
+ Math.Algebra.Hspray: gcdSpray :: forall a. (Eq a, C a) => Spray a -> Spray a -> Spray a
+ Math.Algebra.Hspray: sprayDivisionRemainder :: forall a. (Eq a, C a) => Spray a -> [Spray a] -> Spray a
- Math.Algebra.Hspray: composeSpray :: (C a, Eq a) => Spray a -> [Spray a] -> Spray a
+ Math.Algebra.Hspray: composeSpray :: forall a. (C a, Eq a) => Spray a -> [Spray a] -> Spray a
- Math.Algebra.Hspray: permuteVariables :: Spray a -> [Int] -> Spray a
+ Math.Algebra.Hspray: permuteVariables :: [Int] -> Spray a -> Spray a
- Math.Algebra.Hspray: sprayDivision :: forall a. (Eq a, C a) => Spray a -> [Spray a] -> Spray a
+ Math.Algebra.Hspray: sprayDivision :: forall a. (Eq a, C a) => Spray a -> Spray a -> (Spray a, Spray a)
- Math.Algebra.Hspray: swapVariables :: Spray a -> (Int, Int) -> Spray a
+ Math.Algebra.Hspray: swapVariables :: (Int, Int) -> Spray a -> Spray a
Files
- CHANGELOG.md +14/−1
- README.md +10/−4
- benchmarks/Main.hs +19/−4
- hspray.cabal +2/−2
- src/Math/Algebra/Hspray.hs +367/−226
- tests/Main.hs +40/−9
CHANGELOG.md view
@@ -75,4 +75,17 @@ * Fixed `resultant` and `subresultants`: the variables of the sprays they return were incorrect. -* New function `gcdQX`, to compute the greatest common divisor of two univariate sprays with rational coefficients.+* New function `gcdQX`, to compute the greatest common divisor of two univariate sprays with rational coefficients. + + +## 0.2.4.0 - 2024-03-30 + +* Flipped the order of the arguments in `permuteVariables` and `swapVariables`. + +* New function `gcdSpray`, to compute the greatest common divisor of two sprays with coefficients in a field. + +* The function `gcdQX` has been removed since `gcdSpray` is more general. + +* The function `sprayDivision` has been renamed to `sprayDivisionRemainder`. + +* New function `sprayDivision`, returning the quotient and the remainder of the division of two sprays.
README.md view
@@ -5,7 +5,7 @@ [](https://github.com/stla/hspray/actions/workflows/Stack-nightly.yml) <!-- badges: end --> -Simple multivariate polynomials in Haskell. +*Simple multivariate polynomials in Haskell.* ___ @@ -47,7 +47,7 @@ -- "((1.0) * a^(2)) * X^(0, 2) + ((2.0) * a^(2)) * X^(1, 1) + ((1.0) * a^(2)) * X^(2)" ``` -Evaluation: +#### Evaluation: ```haskell import Math.Algebra.Hspray @@ -60,7 +60,7 @@ -- 8.0 ``` -Partial evaluation: +#### Partial evaluation: ```haskell import Math.Algebra.Hspray @@ -78,7 +78,7 @@ -- "(6 % 1) + (1 % 1) x2" ``` -Differentiation: +#### Differentiation: ```haskell import Math.Algebra.Hspray @@ -174,3 +174,9 @@ map toList $ map snd l -- [[([0,1],2 % 3)],[([1],1 % 1)],[([1],1 % 1)]] ``` + + +## Other features + +Resultant and subresultants of two polynomials, and greatest common divisor of two polynomials +with coefficients in a field.
benchmarks/Main.hs view
@@ -4,12 +4,27 @@ fibo :: Int -> Integer fibo n = if n < 2 then toInteger n else fibo (n - 1) + fibo (n - 2) +combn2 :: Int -> [(Int, (Int, Int))] +combn2 n = zip range (zip row1 row2) + where + range = [0 .. n-2] + row1 = concat $ scanl1 (++) (map (: []) range) + row2 = concatMap (\i -> replicate i i) [1 .. n-1] + +combn2' :: Int -> [(Int, (Int, Int))] -- winner +combn2' n = zip [0 .. n-2] (zip row1 row2) + where + range = [1 .. n-1] + row1 = concatMap (\i -> [0 .. i-1]) range + row2 = concatMap (\i -> replicate i i) range + main :: IO () main = defaultMain - [ bgroup "Fibonacci numbers" - [ bench "fifth" $ nf fibo 5 - , bench "tenth" $ nf fibo 10 - , bench "twentieth" $ nf fibo 20 + [ bgroup "combn2" + [ bench "combn2 - 20" $ nf combn2 20 + , bench "combn2' - 20" $ nf combn2' 20 + , bench "combn2 - 100" $ nf combn2 100 + , bench "combn2' - 100" $ nf combn2' 100 ] ]
hspray.cabal view
@@ -1,7 +1,7 @@ name: hspray -version: 0.2.3.0 +version: 0.2.4.0 synopsis: Multivariate polynomials. -description: Manipulation of multivariate polynomials on a ring, Gröbner basis, resultant and subresultants. +description: Manipulation of multivariate polynomials on a commutative ring, Gröbner basis, resultant and subresultants, and greatest common divisor. homepage: https://github.com/stla/hspray#readme license: GPL-3 license-file: LICENSE
src/Math/Algebra/Hspray.hs view
@@ -50,6 +50,7 @@ , swapVariables -- * Division of a spray , sprayDivision + , sprayDivisionRemainder -- * Gröbner basis , groebner , reduceGroebnerBasis @@ -62,7 +63,7 @@ , subresultants , subresultants1 -- * Greatest common divisor - , gcdQX + , gcdSpray -- * Miscellaneous , fromList , toList @@ -156,7 +157,8 @@ instance Eq Powers where (==) :: Powers -> Powers -> Bool pows1 == pows2 = exponents pows1' == exponents pows2' - where (pows1', pows2') = harmonize (pows1, pows2) + where + (pows1', pows2') = harmonize (pows1, pows2) instance Hashable Powers where hashWithSalt :: Int -> Powers -> Int @@ -167,16 +169,22 @@ type Monomial a = (Powers, a) instance (AlgAdd.C a, Eq a) => AlgAdd.C (Spray a) where - p + q = addSprays p q + (+) :: Spray a -> Spray a -> Spray a + p + q = addSprays p q + zero :: Spray a zero = HM.empty + negate :: Spray a -> Spray a negate = negateSpray instance (AlgRing.C a, Eq a) => AlgMod.C a (Spray a) where + (*>) :: a -> Spray a -> Spray a lambda *> p = scaleSpray lambda p instance (AlgRing.C a, Eq a) => AlgRing.C (Spray a) where + (*) :: Spray a -> Spray a -> Spray a p * q = multSprays p q - one = lone 0 + one :: Spray a + one = lone 0 {- instance (AlgRing.C a, Eq a) => Num (Spray a) where p + q = addSprays p q @@ -201,7 +209,9 @@ -- | Power of a spray (^**^) :: (AlgRing.C a, Eq a) => Spray a -> Int -> Spray a -(^**^) p n = AlgRing.product (replicate n p) +(^**^) p n = if n >= 0 + then AlgRing.product (replicate n p) + else error "(^**^): negative power of a spray is not allowed." -- | Scale spray by a scalar (*^) :: (AlgRing.C a, Eq a) => a -> Spray a -> Spray a @@ -218,7 +228,8 @@ -- | drop trailing zeros simplifyPowers :: Powers -> Powers simplifyPowers pows = Powers s (S.length s) - where s = dropWhileR (== 0) (exponents pows) + where + s = dropWhileR (== 0) (exponents pows) -- | drop trailing zeros in the powers of a spray simplifySpray :: Spray a -> Spray a @@ -231,7 +242,8 @@ -- | addition of two sprays addSprays :: (AlgAdd.C a, Eq a) => Spray a -> Spray a -> Spray a addSprays p q = cleanSpray $ HM.foldlWithKey' f p q - where f s powers coef = HM.insertWith (AlgAdd.+) powers coef s + where + f s powers coef = HM.insertWith (AlgAdd.+) powers coef s -- | opposite spray negateSpray :: AlgAdd.C a => Spray a -> Spray a @@ -258,8 +270,8 @@ derivSpray :: (AlgRing.C a, Eq a) => Int -- ^ index of the variable of differentiation (starting at 1) - -> Spray a -- ^ the spray - -> Spray a + -> Spray a -- ^ the spray to be derivated + -> Spray a -- ^ the derivated spray derivSpray i p = if i >= 1 then cleanSpray $ HM.fromListWith (AlgAdd.+) monomials else error "derivSpray: invalid index." @@ -333,7 +345,7 @@ getCoefficient expnts spray = fromMaybe AlgAdd.zero (HM.lookup powers spray) where expnts' = S.dropWhileR (== 0) (S.fromList expnts) - powers = Powers expnts' (S.length expnts') + powers = Powers expnts' (S.length expnts') -- | number of variables in a spray numberOfVariables :: Spray a -> Int @@ -346,7 +358,8 @@ evalMonomial :: AlgRing.C a => [a] -> Monomial a -> a evalMonomial xyz (powers, coeff) = coeff AlgRing.* AlgRing.product (zipWith (AlgRing.^) xyz pows) - where pows = DF.toList (fromIntegral <$> exponents powers) + where + pows = DF.toList (fromIntegral <$> exponents powers) -- | Evaluates a spray -- @@ -368,14 +381,14 @@ substituteMonomial :: AlgRing.C a => [Maybe a] -> Monomial a -> Monomial a substituteMonomial subs (powers, coeff) = (powers'', coeff') where - pows = exponents powers - n = nvariables powers - indices = findIndices isJust (take n subs) - pows' = [fromIntegral (pows `index` i) | i <- indices] - xyz = [fromJust (subs !! i) | i <- indices] - coeff' = coeff AlgRing.* AlgRing.product (zipWith (AlgRing.^) xyz pows') - f i a = if i `elem` indices then 0 else a - pows'' = S.mapWithIndex f pows + pows = exponents powers + n = nvariables powers + indices = findIndices isJust (take n subs) + pows' = [fromIntegral (pows `index` i) | i <- indices] + xyz = [fromJust (subs !! i) | i <- indices] + coeff' = coeff AlgRing.* AlgRing.product (zipWith (AlgRing.^) xyz pows') + f i a = if i `elem` indices then 0 else a + pows'' = S.mapWithIndex f pows powers'' = simplifyPowers $ Powers pows'' n -- | Substitutes some variables in a spray @@ -392,9 +405,10 @@ then spray' else error "substituteSpray: incorrect length of the substitutions list." where - n = numberOfVariables spray + n = numberOfVariables spray monomials = HM.toList spray - spray' = foldl1' (^+^) (map (fromMonomial . substituteMonomial subs) monomials) + spray' = + foldl1' (^+^) (map (fromMonomial . substituteMonomial subs) monomials) -- | Converts a spray with rational coefficients to a spray with double coefficients -- (useful for evaluation) @@ -410,13 +424,13 @@ -- >>> q = composeSpray p [z, x ^+^ y ^+^ z] -- >>> putStrLn $ prettySprayXYZ q -- (1) X + (1) Y + (2) Z -composeSpray :: (AlgRing.C a, Eq a) => Spray a -> [Spray a] -> Spray a +composeSpray :: forall a. (AlgRing.C a, Eq a) => Spray a -> [Spray a] -> Spray a composeSpray p = evalSpray (identify p) where - ---- identify :: (AlgRing.C a, Eq a) => Spray a -> Spray (Spray a) + identify :: Spray a -> Spray (Spray a) identify = HM.map constantSpray --- | Creates a spray from list of terms +-- | Creates a spray from a list of terms fromList :: (AlgRing.C a, Eq a) => [([Int], a)] -> Spray a fromList x = cleanSpray $ HM.fromList $ map (\(expts, coef) -> (Powers (S.fromList expts) (length expts), coef)) x @@ -430,30 +444,31 @@ -- >>> x3 = lone 3 :: Spray Rational -- >>> p = f x1 x2 x3 -- --- prop> permuteVariables p [3, 1, 2] == f x3 x1 x2 -permuteVariables :: Spray a -> [Int] -> Spray a -permuteVariables spray permutation = +-- prop> permuteVariables [3, 1, 2] p == f x3 x1 x2 +permuteVariables :: [Int] -> Spray a -> Spray a +permuteVariables permutation spray = if n' >= n && isPermutation permutation then spray' else error "permuteVariables: invalid permutation." where - n = numberOfVariables spray + n = numberOfVariables spray n' = maximum permutation isPermutation pmtn = minimum pmtn == 1 && length (nub pmtn) == n' - intmap = IM.fromList (zip permutation [1 .. n']) + intmap = IM.fromList (zip permutation [1 .. n']) invpermutation = [intmap IM.! i | i <- [1 .. n']] - permuteSeq x = S.mapWithIndex (\i _ -> x `index` (invpermutation !! i - 1)) x + permuteSeq x = + S.mapWithIndex (\i _ -> x `index` (invpermutation !! i - 1)) x (powers, coeffs) = unzip (HM.toList spray) - expnts = map exponents powers + expnts = map exponents powers expnts' = map (permuteSeq . growSequence' n') expnts powers' = map (\exps -> simplifyPowers (Powers exps n')) expnts' - spray' = HM.fromList (zip powers' coeffs) + spray' = HM.fromList (zip powers' coeffs) -- | Swaps two variables of a spray -- --- prop> swapVariables p (1, 3) == permuteVariables p [3, 2, 1] -swapVariables :: Spray a -> (Int, Int) -> Spray a -swapVariables spray (i, j) = +-- prop> swapVariables (1, 3) p == permuteVariables [3, 2, 1] p +swapVariables :: (Int, Int) -> Spray a -> Spray a +swapVariables (i, j) spray = if i>=1 && j>=1 then spray' else error "swapVariables: invalid indices." @@ -463,12 +478,13 @@ | k == j = i | otherwise = k transposition = map f [1 .. n] - permuteSeq x = S.mapWithIndex (\ii _ -> x `index` (transposition !! ii - 1)) x + permuteSeq x = + S.mapWithIndex (\ii _ -> x `index` (transposition !! ii - 1)) x (powers, coeffs) = unzip (HM.toList spray) - expnts = map exponents powers + expnts = map exponents powers expnts' = map (permuteSeq . growSequence' n) expnts powers' = map (\exps -> simplifyPowers (Powers exps n)) expnts' - spray' = HM.fromList (zip powers' coeffs) + spray' = HM.fromList (zip powers' coeffs) -- pretty stuff --------------------------------------------------------------- @@ -495,8 +511,9 @@ -> String prettySpray prettyCoef var p = unpack $ intercalate (pack " + ") stringTerms where - stringTerms = map stringTerm (sortBy (flip compare `on` fexpts) (HM.toList p)) - fexpts term = exponents $ fst term + stringTerms = + map stringTerm (sortBy (flip compare `on` fexpts) (HM.toList p)) + fexpts term = exponents $ fst term stringTerm term = append (snoc (snoc (cons '(' $ snoc stringCoef ')') ' ') '*') (prettyPowers var pows) @@ -510,8 +527,8 @@ where n = S.length pows f i p - | p == 0 = "" - | p == 1 = "x" ++ show i + | p == 0 = "" + | p == 1 = "x" ++ show i | otherwise = "x" ++ show i ++ "^" ++ show p x1x2x3 = concatMap (\i -> f i (pows `index` (i-1))) [1 .. n] @@ -526,14 +543,15 @@ prettySpray' :: (Show a) => Spray a -> String prettySpray' spray = unpack $ intercalate (pack " + ") terms where - terms = map stringTerm (sortBy (flip compare `on` fexpts) (HM.toList spray)) - fexpts term = exponents $ fst term + terms = map stringTerm + (sortBy (flip compare `on` fexpts) (HM.toList spray)) + fexpts term = exponents $ fst term stringTerm term = append stringCoef'' (prettyPowers' pows) where - pows = exponents (fst term) - constant = S.length pows == 0 - stringCoef = pack $ show (snd term) - stringCoef' = cons '(' $ snoc stringCoef ')' + pows = exponents (fst term) + constant = S.length pows == 0 + stringCoef = pack $ show (snd term) + stringCoef' = cons '(' $ snoc stringCoef ')' stringCoef'' = if constant then stringCoef' else snoc stringCoef' ' ' -- | prettyPowersXYZ [1, 2, 1] = XY^2Z @@ -542,15 +560,15 @@ then pack xyz else error "there is more than three variables" where - n = S.length pows + n = S.length pows gpows = growSequence pows n 3 f letter p - | p == 0 = "" - | p == 1 = letter + | p == 0 = "" + | p == 1 = letter | otherwise = letter ++ "^" ++ show p - x = f "X" (gpows `index` 0) - y = f "Y" (gpows `index` 1) - z = f "Z" (gpows `index` 2) + x = f "X" (gpows `index` 0) + y = f "Y" (gpows `index` 1) + z = f "Z" (gpows `index` 2) xyz = x ++ y ++ z -- | Pretty form of a spray having at more three variables @@ -599,14 +617,14 @@ -- | index of the maximum of a list maxIndex :: Ord a => [a] -> Int -maxIndex = fst . maximumBy (comparing snd) . zip [0..] +maxIndex = fst . maximumBy (comparing snd) . zip [0 .. ] -- | Leading term of a spray leadingTerm :: Spray a -> Monomial a leadingTerm p = (biggest, p HM.! biggest) where - powers = HM.keys p - i = maxIndex $ map exponents powers + powers = HM.keys p + i = maxIndex $ map exponents powers biggest = powers !! i -- | whether a monomial divides another monomial @@ -615,26 +633,26 @@ where expntsP = exponents powsP expntsQ = exponents powsQ - lower = DF.all (\(x, y) -> x <= y) (S.zip expntsP expntsQ) + lower = DF.all (uncurry (<=)) (S.zip expntsP expntsQ) -- | quotient of monomial Q by monomial p, assuming P divides Q quotient :: AlgField.C a => Monomial a -> Monomial a -> Monomial a quotient (powsQ, coeffQ) (powsP, coeffP) = (pows, coeff) where (powsP', powsQ') = harmonize (powsP, powsQ) - expntsP = exponents powsP' - expntsQ = exponents powsQ' - expnts = S.zipWith (-) expntsQ expntsP - n = nvariables powsP' - pows = Powers expnts n - coeff = coeffQ AlgField./ coeffP + expntsP = exponents powsP' + expntsQ = exponents powsQ' + expnts = S.zipWith (-) expntsQ expntsP + n = nvariables powsP' + pows = Powers expnts n + coeff = coeffQ AlgField./ coeffP -- | Remainder of the division of a spray by a list of divisors, -- using the lexicographic ordering of the monomials -sprayDivision :: forall a. (Eq a, AlgField.C a) => Spray a -> [Spray a] -> Spray a -sprayDivision p qs = +sprayDivisionRemainder :: forall a. (Eq a, AlgField.C a) => Spray a -> [Spray a] -> Spray a +sprayDivisionRemainder p qs = if n == 0 - then error "sprayDivision: the list of divisors is empty." + then error "sprayDivisionRemainder: the list of divisors is empty." else snd $ ogo p AlgAdd.zero where n = length qs @@ -646,27 +664,59 @@ go :: Monomial a -> Spray a -> Spray a -> Int -> Bool -> (Spray a, Spray a) go lts !s r !i !divoccured | divoccured = (s, r) - | i == n = g lts s r - | otherwise = go lts news r (i+1) newdivoccured + | i == n = g lts s r + | otherwise = go lts news r (i+1) newdivoccured where - (q, ltq) = qsltqs !! i + (q, ltq) = qsltqs !! i newdivoccured = divides ltq lts - news = if newdivoccured + news = if newdivoccured then s ^-^ (fromMonomial (quotient lts ltq) ^*^ q) else s ogo :: Spray a -> Spray a -> (Spray a, Spray a) ogo !s !r | s == AlgAdd.zero = (s, r) - | otherwise = ogo s' r' + | otherwise = ogo s' r' where (s', r') = go (leadingTerm s) s r 0 False +-- | Division of a spray by a spray +sprayDivision :: forall a. (Eq a, AlgField.C a) + => Spray a -- ^ dividend + -> Spray a -- ^ divisor + -> (Spray a, Spray a) -- ^ (quotient, remainder) +sprayDivision sprayA sprayB = + if sprayB == AlgAdd.zero + then error "sprayDivision: division by zero." + else ogo sprayA AlgAdd.zero AlgAdd.zero + where + go :: Monomial a -> Spray a -> Spray a -> Spray a -> Int -> Bool -> (Spray a, Spray a, Spray a) + go ltp !p !q r !i !divoccured + | divoccured = (p, q, r) + | i == 1 = (p ^-^ ltpspray, q, r ^+^ ltpspray) + | otherwise = go ltp newp newq r 1 newdivoccured + where + ltpspray = fromMonomial ltp + ltB = leadingTerm sprayB + newdivoccured = divides ltB ltp + (newp, newq) = if newdivoccured + then (p ^-^ (qtnt ^*^ sprayB), q ^+^ qtnt) + else (p, q) + where + qtnt = fromMonomial $ quotient ltp ltB + ogo :: Spray a -> Spray a -> Spray a -> (Spray a, Spray a) + ogo !p !q !r + | p == AlgAdd.zero = (q, r) + | otherwise = ogo p' q' r' + where + (p', q', r') = go (leadingTerm p) p q r 0 False + -- Groebner stuff ------------------------------------------------------------- --- | slight modification of `sprayDivision` to speed up groebner00 -sprayDivision' :: forall a. (Eq a, AlgField.C a) => Spray a -> HashMap Int (Spray a, Monomial a) -> Spray a -sprayDivision' p qsltqs = snd $ ogo p AlgAdd.zero +-- | slight modification of `sprayDivisionRemainder` to speed up groebner00 +sprayDivisionRemainder' :: forall a. (Eq a, AlgField.C a) + => Spray a -> HashMap Int (Spray a, Monomial a) -> Spray a +sprayDivisionRemainder' p qsltqs = snd $ ogo p AlgAdd.zero where n = HM.size qsltqs g :: Monomial a -> Spray a -> Spray a -> (Spray a, Spray a) @@ -676,10 +726,10 @@ go :: Monomial a -> Spray a -> Spray a -> Int -> Bool -> (Spray a, Spray a) go lts !s r !i !divoccured | divoccured = (s, r) - | i == n = g lts s r - | otherwise = go lts news r (i+1) newdivoccured + | i == n = g lts s r + | otherwise = go lts news r (i+1) newdivoccured where - (q, ltq) = qsltqs HM.! i + (q, ltq) = qsltqs HM.! i newdivoccured = divides ltq lts news = if newdivoccured then s ^-^ (fromMonomial (quotient lts ltq) ^*^ q) @@ -687,30 +737,34 @@ ogo :: Spray a -> Spray a -> (Spray a, Spray a) ogo !s !r | s == AlgAdd.zero = (s, r) - | otherwise = ogo s' r' + | otherwise = ogo s' r' where (s', r') = go (leadingTerm s) s r 0 False +-- combinations of two among n combn2 :: Int -> Int -> HashMap Int (Int, Int) -combn2 n s = HM.fromList (zip [0 .. n-2] (zip row1 row2)) +combn2 n s = HM.fromList (zip range0 (zip row1 row2)) where - row1 = drop s $ concatMap (\i -> [0 .. (i-1)]) [1 .. n-1] - row2 = drop s $ concatMap (\i -> replicate i i) [1 .. n-1] + range0 = [0 .. n-2] + range1 = [1 .. n-1] + row1 = drop s $ concatMap (\i -> [0 .. i-1]) range1 + row2 = drop s $ concatMap (\i -> replicate i i) range1 +-- the "S polynomial" sPolynomial :: (Eq a, AlgField.C a) => (Spray a, Monomial a) -> (Spray a, Monomial a) -> Spray a sPolynomial pltp qltq = wp ^*^ p ^-^ wq ^*^ q where - p = fst pltp - q = fst qltq - (lpowsP, lcoefP) = snd pltp - (lpowsQ, lcoefQ) = snd qltq + p = fst pltp + q = fst qltq + (lpowsP, lcoefP) = snd pltp + (lpowsQ, lcoefQ) = snd qltq (lpowsP', lpowsQ') = harmonize (lpowsP, lpowsQ) - lexpntsP = exponents lpowsP' - lexpntsQ = exponents lpowsQ' + lexpntsP = exponents lpowsP' + lexpntsQ = exponents lpowsQ' gamma = S.zipWith max lexpntsP lexpntsQ betaP = S.zipWith (-) gamma lexpntsP betaQ = S.zipWith (-) gamma lexpntsQ - n = nvariables lpowsP' + n = nvariables lpowsP' wp = fromMonomial (Powers betaP n, AlgField.recip lcoefP) wq = fromMonomial (Powers betaQ n, AlgField.recip lcoefQ) @@ -718,19 +772,19 @@ groebner00 :: forall a. (Eq a, AlgField.C a) => [Spray a] -> [Spray a] groebner00 sprays = go 0 j0 combins0 spraysMap where - j0 = length sprays + j0 = length sprays combins0 = combn2 j0 0 - ltsprays = map leadingTerm sprays + ltsprays = map leadingTerm sprays spraysltsprays = zip sprays ltsprays - spraysMap = HM.fromList (zip [0 .. j0-1] spraysltsprays) + spraysMap = HM.fromList (zip [0 .. j0-1] spraysltsprays) go :: Int -> Int -> HashMap Int (Int, Int) -> HashMap Int (Spray a, Monomial a) -> [Spray a] go !i !j !combins !gpolysMap | i == length combins = map fst (HM.elems gpolysMap) - | otherwise = go i' j' combins' gpolysMap' + | otherwise = go i' j' combins' gpolysMap' where - (k, l) = combins HM.! i - sfg = sPolynomial (gpolysMap HM.! k) (gpolysMap HM.! l) - sbarfg = sprayDivision' sfg gpolysMap + (k, l) = combins HM.! i + sfg = sPolynomial (gpolysMap HM.! k) (gpolysMap HM.! l) + sbarfg = sprayDivisionRemainder' sfg gpolysMap ltsbarfg = leadingTerm sbarfg (i', j', gpolysMap', combins') = if sbarfg == AlgAdd.zero then @@ -747,20 +801,20 @@ groebner0 sprays = if n <= 1 then sprays else [basis00 !! k | k <- [0 .. n-1] \\ discard] where - n = length basis00 + n = length basis00 basis00 = groebner00 sprays go :: Int -> [Int] -> [Int] go !i toRemove - | i == n = toRemove + | i == n = toRemove | otherwise = go (i+1) toRemove' where - ltf = leadingTerm (basis00 !! i) + ltf = leadingTerm (basis00 !! i) toDrop = toRemove ++ [i] igo :: Int -> Bool igo !j - | j == n = False + | j == n = False | j `elem` toDrop = igo (j+1) - | otherwise = ok || igo (j+1) + | otherwise = ok || igo (j+1) where ok = divides (leadingTerm (basis00 !! j)) ltf toRemove' = if igo 0 then toDrop else toRemove @@ -769,22 +823,24 @@ -- | Reduces a Groebner basis reduceGroebnerBasis :: forall a. (Eq a, AlgField.C a) => [Spray a] -> [Spray a] reduceGroebnerBasis gbasis = - if length gbasis >= 2 then map reduction [0 .. n-1] else ngbasis + if length gbasis >= 2 + then map reduction [0 .. n-1] + else ngbasis where normalize :: Spray a -> Spray a normalize spray = AlgField.recip coef *^ spray where (_, coef) = leadingTerm spray ngbasis = map normalize gbasis - n = length ngbasis + n = length ngbasis reduction :: Int -> Spray a - reduction i = sprayDivision (ngbasis !! i) rest + reduction i = sprayDivisionRemainder (ngbasis !! i) rest where rest = [ngbasis !! k | k <- [0 .. n-1] \\ [i]] -- | Groebner basis (always minimal and possibly reduced) -- --- prop> groebner ps True = reduceGroebnerBasis (groebner ps False) +-- prop> groebner ps True == reduceGroebnerBasis (groebner ps False) groebner :: forall a. (Eq a, AlgField.C a) => [Spray a] -- ^ list of sprays @@ -816,12 +872,12 @@ | otherwise = map (q:) cs ++ run (n-1) i qs (drop dc cs) where f :: [a] -> (a, [a]) - f [] = error "should not happen" + f [] = error "combinationsOf: should not happen." f (b:bs) = (b, bs) - (q, qs) = f (take (n-i+1) ys) - dc = product [(n-k+1) .. (n-1)] `div` product [1 .. i-1] + (q, qs) = f (take (n-i+1) ys) + dc = product [(n-k+1) .. (n-1)] `div` product [1 .. i-1] --- | generate all permutations of a binary sequence +-- | generates all permutations of a binary sequence permutationsBinarySequence :: Int -> Int -> [Seq Int] permutationsBinarySequence nzeros nones = let n = nzeros + nones in @@ -844,8 +900,8 @@ -> Spray a esPolynomial n k | k <= 0 || n <= 0 = error "esPolynomial: both arguments must be positive integers." - | k > n = AlgAdd.zero - | otherwise = simplifySpray spray + | k > n = AlgAdd.zero + | otherwise = simplifySpray spray where perms = permutationsBinarySequence (n-k) k spray = HM.fromList $ map (\expts -> (Powers expts n, AlgRing.one)) perms @@ -856,15 +912,13 @@ where n = numberOfVariables spray indices = [1 .. n] - esPolys = map (\i -> esPolynomial n i :: Spray a) indices - yPolys = map (\i -> lone (n + i) :: Spray a) indices - gPolys = zipWith (^-^) esPolys yPolys - gbasis = groebner0 gPolys - g = sprayDivision spray gbasis + gPolys = map (\i -> esPolynomial n i ^-^ lone (n + i)) indices + gbasis = groebner0 gPolys + g = sprayDivisionRemainder spray gbasis gpowers = HM.keys g - check1 = minimum (map nvariables gpowers) > n - expnts = map exponents gpowers - check2 = DF.all (DF.all (0 ==)) (map (S.take n) expnts) + check1 = minimum (map nvariables gpowers) > n + expnts = map exponents gpowers + check2 = DF.all (DF.all (0 ==)) (map (S.take n) expnts) -- | Whether a spray can be written as a polynomial of a given list of sprays -- (the sprays in the list must belong to the same polynomial ring as the spray); @@ -883,21 +937,21 @@ n = numberOfVariables spray n' = maximum $ map numberOfVariables sprays result - | n > n' = (False, Nothing) - | n < n' = error "not enough variables in the spray" + | n > n' = (False, Nothing) + | n < n' = error "not enough variables in the spray" | otherwise = (checks, poly) where - m = length sprays - yPolys = map (\i -> lone (n + i) :: Spray a) [1 .. m] - gPolys = zipWith (^-^) sprays yPolys + m = length sprays + yPolys = map (\i -> lone (n + i) :: Spray a) [1 .. m] + gPolys = zipWith (^-^) sprays yPolys gbasis0 = groebner0 gPolys - g = sprayDivision spray gbasis0 + g = sprayDivisionRemainder spray gbasis0 gpowers = HM.keys g - check1 = minimum (map nvariables gpowers) > n - expnts = map exponents gpowers - check2 = DF.all (DF.all (0 ==)) (map (S.take n) expnts) - checks = check1 && check2 - poly = if checks + check1 = minimum (map nvariables gpowers) > n + expnts = map exponents gpowers + check2 = DF.all (DF.all (0 ==)) (map (S.take n) expnts) + checks = check1 && check2 + poly = if checks then Just $ dropXis g else Nothing dropXis = HM.mapKeys f @@ -912,28 +966,34 @@ where m = length x - 1 n = length y - 1 - xrows = [replicate i AlgAdd.zero ++ x ++ replicate (n-i-1) AlgAdd.zero | i <- [0 .. n-1]] - yrows = [replicate i AlgAdd.zero ++ y ++ replicate (m-i-1) AlgAdd.zero | i <- [0 .. m-1]] + xrows = [replicate i AlgAdd.zero ++ x ++ replicate (n-i-1) AlgAdd.zero + | i <- [0 .. n-1]] + yrows = [replicate i AlgAdd.zero ++ y ++ replicate (m-i-1) AlgAdd.zero + | i <- [0 .. m-1]] -- "truncated" Sylvester matrix sylvesterMatrix' :: AlgRing.C a => [a] -> [a] -> Int -> Matrix a sylvesterMatrix' x y k = if s == 0 - then fromLists [[AlgRing.one]] -- plays the role of the empty matrix: determinant=1 (because the empty matrix is not allowed) + then fromLists [[AlgRing.one]] -- plays the role of the empty matrix: determinant=1 + -- (because the empty matrix is not allowed in the matrix package) else submatrix 1 s 1 s $ fromLists (xrows ++ yrows) where m = length x - 1 n = length y - 1 s = m + n - 2*k - xrows = [replicate i AlgAdd.zero ++ x ++ replicate (n-i-1) AlgAdd.zero | i <- [0 .. n-1-k]] - yrows = [replicate i AlgAdd.zero ++ y ++ replicate (m-i-1) AlgAdd.zero | i <- [0 .. m-1-k]] + xrows = [replicate i AlgAdd.zero ++ x ++ replicate (n-i-1) AlgAdd.zero + | i <- [0 .. n-1-k]] + yrows = [replicate i AlgAdd.zero ++ y ++ replicate (m-i-1) AlgAdd.zero + | i <- [0 .. m-1-k]] -- determinant detLaplace :: forall a. (Eq a, AlgRing.C a) => Matrix a -> a detLaplace m = if nrows m == 1 then m DM.! (1,1) - else suml1 [negateIf i (times (m DM.! (i,1)) (detLaplace (minorMatrix i 1 m))) | i <- [1 .. nrows m]] + else suml1 [negateIf i (times (m DM.! (i,1)) (detLaplace (minorMatrix i 1 m))) + | i <- [1 .. nrows m]] where - suml1 = foldl1' (AlgAdd.+) + suml1 = foldl1' (AlgAdd.+) negateIf i = if even i then AlgAdd.negate else id times :: a -> a -> a times x y = if x == AlgAdd.zero then AlgAdd.zero else x AlgRing.* y @@ -947,18 +1007,19 @@ where n = numberOfVariables spray (powers, coeffs) = unzip (HM.toList spray) - expnts = map exponents powers + expnts = map exponents powers constantTerm = fromMaybe AlgAdd.zero (HM.lookup (Powers S.empty 0) spray) - (expnts', coeffs') = unzip $ filter (\(s,_) -> S.length s > 0) (zip expnts coeffs) - xpows = map (`index` 0) expnts' - expnts'' = map (S.deleteAt 0) expnts' - powers'' = map (\s -> Powers s (S.length s)) expnts'' - sprays'' = zipWith (curry fromMonomial) powers'' coeffs' - imap = IM.fromListWith (^+^) (zip xpows sprays'') - imap' = IM.insertWith (^+^) 0 (constantSpray constantTerm) imap + (expnts', coeffs') = + unzip $ filter (\(s,_) -> S.length s > 0) (zip expnts coeffs) + xpows = map (`index` 0) expnts' + expnts'' = map (S.deleteAt 0) expnts' + powers'' = map (\s -> Powers s (S.length s)) expnts'' + sprays'' = zipWith (curry fromMonomial) powers'' coeffs' + imap = IM.fromListWith (^+^) (zip xpows sprays'') + imap' = IM.insertWith (^+^) 0 (constantSpray constantTerm) imap permutation = [2 .. n] ++ [1] sprays = [ - permuteVariables (fromMaybe AlgAdd.zero (IM.lookup i imap')) permutation + permuteVariables permutation (fromMaybe AlgAdd.zero (IM.lookup i imap')) | i <- [0 .. maximum xpows] ] @@ -974,12 +1035,18 @@ qexpnts = map (`index` 0) $ filter (not . S.null) (map exponents (HM.keys q)) p0 = fromMaybe AlgAdd.zero (HM.lookup (Powers S.empty 0) p) q0 = fromMaybe AlgAdd.zero (HM.lookup (Powers S.empty 0) q) - pcoeffs = reverse $ if null pexpnts + pcoeffs = if null pexpnts then [p0] - else p0 : [fromMaybe AlgAdd.zero (HM.lookup (Powers (S.singleton i) 1) p) | i <- [1 .. maximum pexpnts]] - qcoeffs = reverse $ if null qexpnts + else [fromMaybe AlgAdd.zero (HM.lookup (Powers (S.singleton i) 1) p) + | i <- [maxp, maxp-1 .. 1]] ++ [p0] + where + maxp = maximum pexpnts + qcoeffs = if null qexpnts then [q0] - else q0 : [fromMaybe AlgAdd.zero (HM.lookup (Powers (S.singleton i) 1) q) | i <- [1 .. maximum qexpnts]] + else [fromMaybe AlgAdd.zero (HM.lookup (Powers (S.singleton i) 1) q) + | i <- [maxq, maxq-1 .. 1]] ++ [q0] + where + maxq = maximum qexpnts -- | Subresultants of two /univariate/ sprays subresultants1 :: (Eq a, AlgRing.C a) => Spray a -> Spray a -> [a] @@ -992,12 +1059,18 @@ qexpnts = map (`index` 0) $ filter (not . S.null) (map exponents (HM.keys q)) p0 = fromMaybe AlgAdd.zero (HM.lookup (Powers S.empty 0) p) q0 = fromMaybe AlgAdd.zero (HM.lookup (Powers S.empty 0) q) - pcoeffs = reverse $ if null pexpnts + pcoeffs = if null pexpnts then [p0] - else p0 : [fromMaybe AlgAdd.zero (HM.lookup (Powers (S.singleton i) 1) p) | i <- [1 .. maximum pexpnts]] - qcoeffs = reverse $ if null qexpnts + else [fromMaybe AlgAdd.zero (HM.lookup (Powers (S.singleton i) 1) p) + | i <- [maxp, maxp-1 .. 1]] ++ [p0] + where + maxp = maximum pexpnts + qcoeffs = if null qexpnts then [q0] - else q0 : [fromMaybe AlgAdd.zero (HM.lookup (Powers (S.singleton i) 1) q) | i <- [1 .. maximum qexpnts]] + else [fromMaybe AlgAdd.zero (HM.lookup (Powers (S.singleton i) 1) q) + | i <- [maxq, maxq-1 .. 1]] ++ [q0] + where + maxq = maximum qexpnts d = length pcoeffs e = length qcoeffs @@ -1009,15 +1082,16 @@ -> Spray a resultant var p q = if var >= 1 && var <= n - then permuteVariables det permutation' + then permuteVariables permutation' det else error "resultant: invalid variable index." where n = max (numberOfVariables p) (numberOfVariables q) - permutation = var : [1 .. var-1] ++ [var+1 .. n] + permutation = var : [1 .. var-1] ++ [var+1 .. n] permutation' = [2 .. var] ++ (1 : [var+1 .. n]) - p' = permuteVariables p permutation - q' = permuteVariables q permutation - det = detLaplace $ sylvesterMatrix (sprayCoefficients p') (sprayCoefficients q') + p' = permuteVariables permutation p + q' = permuteVariables permutation q + det = detLaplace $ + sylvesterMatrix (sprayCoefficients p') (sprayCoefficients q') -- | Subresultants of two sprays subresultants :: (Eq a, AlgRing.C a) @@ -1028,7 +1102,8 @@ subresultants var p q | var < 1 = error "subresultants: invalid variable index." | var > n = error "subresultants: too large variable index." - | otherwise = map (permute . detLaplace . sylvesterMatrix' pcoeffs qcoeffs) [0 .. min d e - 1] + | otherwise = map (permute' . detLaplace . sylvesterMatrix' pcoeffs qcoeffs) + [0 .. min d e - 1] where pcoeffs = sprayCoefficients p' qcoeffs = sprayCoefficients q' @@ -1036,92 +1111,158 @@ e = length qcoeffs n = max (numberOfVariables p) (numberOfVariables q) permutation = var : [1 .. var-1] ++ [var+1 .. n] - p' = permuteVariables p permutation - q' = permuteVariables q permutation + permute = permuteVariables permutation + p' = permute p + q' = permute q permutation' = [2 .. var] ++ (1 : [var+1 .. n]) - permute spray = permuteVariables spray permutation' + permute' = permuteVariables permutation' --- GCD ------------------------------------------------------------------------ +-- GCD stuff ------------------------------------------------------------------ --- the degree of a spray as a univariate spray in x with spray coefficients -degree :: Eq a => Spray a -> Int -degree spray = - if numberOfVariables spray == 0 - then - if spray == HM.empty then minBound else 0 - else maximum xpows +-- the coefficients of a spray as a univariate spray in x_n with spray coefficients +sprayCoefficients' :: (Eq a, AlgRing.C a) => Int -> Spray a -> [Spray a] +sprayCoefficients' n spray + | numberOfVariables spray /= n = [spray] + | n == 0 = [constantSpray constantTerm] + | otherwise = sprays where - expnts = map exponents $ HM.keys spray - expnts' = filter (not . S.null) expnts + permutation = [2 .. n] ++ [1] + spray' = permuteVariables permutation spray + (powers, coeffs) = unzip (HM.toList spray') + expnts = map exponents powers + constantTerm = fromMaybe AlgAdd.zero (HM.lookup (Powers S.empty 0) spray') + (expnts', coeffs') = + unzip $ filter (\(s,_) -> (not . S.null) s) (zip expnts coeffs) xpows = map (`index` 0) expnts' + expnts'' = map (S.deleteAt 0) expnts' + powers'' = map (\s -> Powers s (S.length s)) expnts'' + sprays'' = zipWith (curry fromMonomial) powers'' coeffs' + imap = IM.fromListWith (^+^) (zip xpows sprays'') + imap' = IM.insertWith (^+^) 0 (constantSpray constantTerm) imap + deg = maximum xpows + sprays = [ + fromMaybe AlgAdd.zero (IM.lookup i imap') + | i <- [deg, deg-1 .. 0] + ] --- the degree and the leading coefficient of a spray as a univariate spray in x with spray coefficients -degreeAndLeadingCoefficient :: (Eq a, AlgRing.C a) => Spray a -> (Int, Spray a) -degreeAndLeadingCoefficient spray = - if n == 0 - then (if constantTerm == AlgAdd.zero then minBound else 0, constantSpray constantTerm) - else (deg, leadingCoeff) +-- the degree of a spray as a univariate spray in x_n with spray coefficients +degree :: (Eq a, AlgAdd.C a) => Int -> Spray a -> Int +degree n spray + | numberOfVariables spray == 0 = + if spray == zeroSpray then minBound else 0 + | numberOfVariables spray /= n = 0 + | otherwise = maximum xpows + where + permutation = [2 .. n] ++ [1] + spray' = permuteVariables permutation spray + expnts = map exponents $ HM.keys spray' + expnts' = filter (not . S.null) expnts + xpows = map (`index` 0) expnts' + +-- the degree and the leading coefficient of a spray as a univariate spray +-- in x_n with spray coefficients +degreeAndLeadingCoefficient :: (Eq a, AlgRing.C a) => Int -> Spray a -> (Int, Spray a) +degreeAndLeadingCoefficient n spray + | n == 0 = ( + if constantTerm == AlgAdd.zero + then minBound + else 0, + constantSpray constantTerm + ) + | numberOfVariables spray /= n = (0, spray) + | otherwise = (deg, leadingCoeff) where - n = numberOfVariables spray - (powers, coeffs) = unzip (HM.toList spray) - expnts = map exponents powers - constantTerm = fromMaybe AlgAdd.zero (HM.lookup (Powers S.empty 0) spray) - (expnts', coeffs') = unzip $ filter (\(s,_) -> not $ S.null s) (zip expnts coeffs) + permutation = [2 .. n] ++ [1] + spray' = permuteVariables permutation spray + (powers, coeffs) = unzip (HM.toList spray') + expnts = map exponents powers + constantTerm = fromMaybe AlgAdd.zero (HM.lookup (Powers S.empty 0) spray') + (expnts', coeffs') = + unzip $ filter (\(s,_) -> not $ S.null s) (zip expnts coeffs) xpows = map (`index` 0) expnts' - deg = maximum xpows - is = elemIndices deg xpows - expnts'' = [S.update 0 0 (expnts' !! i) | i <- is] + deg = maximum xpows + is = elemIndices deg xpows + expnts'' = [S.deleteAt 0 (expnts' !! i) | i <- is] powers'' = map (\s -> Powers s (S.length s)) expnts'' coeffs'' = [coeffs' !! i | i <- is] leadingCoeff = foldl1' (^+^) (zipWith (curry fromMonomial) powers'' coeffs'') --- pseudo-division of two sprays (assuming degA >= degB >= 0) -pseudoDivision :: (Eq a, AlgRing.C a) - => Spray a -- ^ A +-- pseudo-division of two sprays, assuming degA >= degB >= 0 +pseudoDivision :: (Eq a, AlgRing.C a) + => Int -- ^ number of variables + -> Spray a -- ^ A -> Spray a -- ^ B -> (Spray a, (Spray a, Spray a)) -- ^ (c, (Q, R)) such that c^*^A = B^*^Q ^+^ R -pseudoDivision sprayA sprayB = (ellB ^**^ delta , go sprayA zeroSpray delta) +pseudoDivision n sprayA sprayB + | degB == minBound = error "pseudoDivision: pseudo-division by 0." + | degA < degB = error "pseudoDivision: degree(A) < degree(B)." + | otherwise = (ellB ^**^ delta , go sprayA zeroSpray delta) where - degA = degree sprayA - (degB, ellB) = degreeAndLeadingCoefficient sprayB - delta = degA - degB + 1 + degA = degree n sprayA + (degB, ellB) = degreeAndLeadingCoefficient n sprayB + delta = degA - degB + 1 go sprayR sprayQ e = - if degR < degB + if degR < degB || sprayR == zeroSpray then (q ^*^ sprayQ, q ^*^ sprayR) - else go (ellB ^*^ sprayR ^-^ sprayS ^*^ sprayB) (ellB ^*^ sprayQ ^+^ sprayS) (e - 1) + else go (ellB ^*^ sprayR ^-^ sprayS ^*^ sprayB) + (ellB ^*^ sprayQ ^+^ sprayS) + (e - 1) where - (degR, ellR) = degreeAndLeadingCoefficient sprayR - q = ellB ^**^ e - sprayX = lone 1 - sprayS = ellR ^*^ sprayX ^**^ (degR - degB) + (degR, ellR) = degreeAndLeadingCoefficient n sprayR + q = ellB ^**^ e + sprayXn = lone n + sprayS = ellR ^*^ sprayXn ^**^ (degR - degB) --- The GCD of two /univariate/ sprays with rational coefficients -gcdQX :: Spray Rational -> Spray Rational -> Spray Rational -gcdQX sprayA sprayB - | n >= 2 = error "gcdQX: the sprays are not univariate." - | degree sprayB > degree sprayA = gcdQX sprayB sprayA - | sprayB == zeroSpray = sprayA - | otherwise = go sprayA' sprayB' 1 1 +-- recursive GCD function +gcdKX1dotsXn :: forall a. (Eq a, AlgField.C a) => Int -> Spray a -> Spray a -> Spray a +gcdKX1dotsXn n sprayA sprayB + | n == 0 = constantSpray $ gcdKX0 sprayA sprayB + | degB > degA = gcdKX1dotsXn n sprayB sprayA + | sprayB == zeroSpray = sprayA + | otherwise = go sprayA' sprayB' unitSpray unitSpray where - n = max (numberOfVariables sprayA) (numberOfVariables sprayB) - coefficients :: Spray Rational -> [Rational] - coefficients spray = map (getCoefficient []) (sprayCoefficients spray) - cont :: Spray Rational -> Rational - cont spray = maximum $ coefficients spray - reduce :: Spray Rational -> Spray Rational - reduce spray = HM.map (/ cont spray) spray - a = cont sprayA - b = cont sprayB - d = max a b - sprayA' = HM.map (/ a) sprayA - sprayB' = HM.map (/ b) sprayB + gcdKX0 :: Spray a -> Spray a -> a + gcdKX0 = const $ const AlgRing.one + n' = max (numberOfVariables sprayA) (numberOfVariables sprayB) + degA = degree n' sprayA + degB = degree n' sprayB + gcdKX1dotsXm = gcdKX1dotsXn (n-1) + content :: Spray a -> Spray a + content spray = foldl1' gcdKX1dotsXm (sprayCoefficients' n' spray) + exactDivisionBy :: Spray a -> Spray a -> Spray a + exactDivisionBy b a = + if snd division == zeroSpray + then fst division + else error "exactDivisionBy: should not happen." + where + division = sprayDivision a b + reduceSpray :: Spray a -> Spray a + reduceSpray spray = exactDivisionBy cntnt spray + where + coeffs = sprayCoefficients' n' spray + cntnt = foldl1' gcdKX1dotsXm coeffs + contA = content sprayA + contB = content sprayB + d = gcdKX1dotsXm contA contB + sprayA' = exactDivisionBy contA sprayA + sprayB' = exactDivisionBy contB sprayB + go :: Spray a -> Spray a -> Spray a -> Spray a -> Spray a go sprayA'' sprayB'' g h - | sprayR == zeroSpray = d *^ reduce sprayB'' - | numberOfVariables sprayR == 0 = constantSpray d - | otherwise = go sprayB'' (HM.map (/ (g*h^delta)) sprayR) ellAq'' (g^delta / h^(delta-1)) + | sprayR == zeroSpray = d ^*^ reduceSpray sprayB'' + | numberOfVariables sprayR == 0 = d + | otherwise = go sprayB'' + (exactDivisionBy (g ^*^ h^**^delta) sprayR) + ellA'' + (exactDivisionBy (h^**^delta) (h ^*^ g^**^delta)) where - (_, (_, sprayR)) = pseudoDivision sprayA'' sprayB'' - (degA'', ellA'') = degreeAndLeadingCoefficient sprayA'' - ellAq'' = getCoefficient [] ellA'' - delta = degA'' - degree sprayB'' + (_, (_, sprayR)) = pseudoDivision n' sprayA'' sprayB'' + (degA'', ellA'') = degreeAndLeadingCoefficient n' sprayA'' + degB'' = degree n' sprayB'' + delta = degA'' - degB'' + +-- | Greatest common divisor of two sprays with coefficients in a field +gcdSpray :: forall a. (Eq a, AlgField.C a) => Spray a -> Spray a -> Spray a +gcdSpray sprayA sprayB = gcdKX1dotsXn n sprayA sprayB + where + n = max (numberOfVariables sprayA) (numberOfVariables sprayB)
tests/Main.hs view
@@ -30,7 +30,8 @@ subresultants, resultant1, subresultants1, - gcdQX + sprayDivision, + gcdSpray ) import Test.Tasty ( defaultMain , testGroup @@ -153,7 +154,7 @@ x2 = lone 2 :: Spray Rational x3 = lone 3 :: Spray Rational p = f x1 x2 x3 - p' = permuteVariables p [3, 1, 2] + p' = permuteVariables [3, 1, 2] p assertEqual "" p' (f x3 x1 x2), testCase "swapVariables" $ do @@ -162,8 +163,8 @@ x2 = lone 2 :: Spray Rational x3 = lone 3 :: Spray Rational p = x1^**^4 ^+^ (2 *^ x2^**^3) ^+^ (3 *^ x3^**^2) ^-^ (4 *^ unitSpray) - p' = permuteVariables p [3, 2, 1] - assertEqual "" p' (swapVariables p (1, 3)), + p' = permuteVariables [3, 2, 1] p + assertEqual "" p' (swapVariables (1, 3) p), testCase "resultant w.r.t x" $ do let @@ -232,16 +233,16 @@ r1 = resultant1 f g assertEqual "" r1 (getCoefficient [] r), - testCase "gcdQX" $ do + testCase "gcdSpray - univariate example" $ do let x = lone 1 :: Spray Rational sprayD = x^**^2 ^+^ unitSpray sprayA = sprayD ^*^ (x^**^4 ^-^ x) sprayB = sprayD ^*^ (2*^x ^+^ unitSpray) - sprayGCD = gcdQX sprayA sprayB - assertEqual "" sprayGCD (2 *^ sprayD), + sprayGCD = gcdSpray sprayA sprayB + assertEqual "" sprayGCD (9 *^ sprayD), - testCase "gcdQX with a constant spray" $ do + testCase "gcdSpray with a constant spray" $ do let x = lone 1 :: Spray Rational sprayA = 3 *^ x^**^4 ^-^ x @@ -249,6 +250,36 @@ b2 = 4 :: Rational sprayB1 = constantSpray b1 sprayB2 = constantSpray b2 - assertBool "" (gcdQX sprayA sprayB1 == constantSpray 3 && gcdQX sprayA sprayB2 == constantSpray 4) + assertBool "" (gcdSpray sprayA sprayB1 == unitSpray && gcdSpray sprayA sprayB2 == unitSpray), + + testCase "sprayDivision" $ do + let + x = lone 1 :: Spray Rational + y = lone 2 :: Spray Rational + sprayB = x^**^2 ^*^ y ^-^ x ^*^ y ^+^ constantSpray 3 + sprayQ = x^**^4 ^-^ x ^+^ y^**^2 + sprayA = sprayB ^*^ sprayQ + assertEqual "" (sprayDivision sprayA sprayB) (sprayQ, zeroSpray), + + testCase "gcdSpray - bivariate example" $ do + let + x = lone 1 :: Spray Rational + y = lone 2 :: Spray Rational + sprayD = x^**^2 ^*^ y ^-^ x ^*^ y ^+^ constantSpray 3 + sprayA = sprayD ^*^ (x^**^4 ^-^ x ^+^ y^**^2) + sprayB = sprayD ^*^ y ^*^ (2*^x ^+^ unitSpray) + g = gcdSpray sprayA sprayB + assertEqual "" g ((1%3) *^ sprayD), + + testCase "gcdSpray - trivariate example" $ do + let + x = lone 1 :: Spray Rational + y = lone 2 :: Spray Rational + z = lone 3 :: Spray Rational + sprayD = x^**^2 ^*^ y ^-^ x ^*^ y ^+^ z ^+^ constantSpray 3 + sprayA = sprayD^**^1 ^*^ (x^**^4 ^-^ x ^+^ y ^+^ x ^*^ y ^*^ z^**^2) + sprayB = sprayD^**^1 ^*^ y ^*^ (2*^x ^+^ unitSpray) ^*^ z + g = gcdSpray sprayA sprayB + assertEqual "" g sprayD ]