hspray 0.2.5.0 → 0.2.6.0
raw patch · 5 files changed
+837/−217 lines, 5 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Math.Algebra.Hspray: (^/^) :: (Eq a, C a) => Polynomial a -> Polynomial a -> RatioOfPolynomials a
+ Math.Algebra.Hspray: collinearSprays :: (Eq a, C a) => Spray a -> Spray a -> Bool
+ Math.Algebra.Hspray: numberOfVariables :: Spray a -> Int
+ Math.Algebra.Hspray: prettyNumSpray :: (Num a, Ord a, Show a) => Spray a -> String
+ Math.Algebra.Hspray: prettyNumSpray' :: (Num a, Ord a, Show a) => Spray a -> String
+ Math.Algebra.Hspray: prettyNumSprayX1X2X3 :: (Num a, Ord a, Show a) => String -> Spray a -> String
+ Math.Algebra.Hspray: prettyNumSprayXYZ :: (Num a, Ord a, Show a) => [String] -> Spray a -> String
+ Math.Algebra.Hspray: prettyQSpray :: QSpray -> String
+ Math.Algebra.Hspray: prettyQSpray' :: QSpray' -> String
+ Math.Algebra.Hspray: prettyQSpray'' :: QSpray -> String
+ Math.Algebra.Hspray: prettyQSpray''' :: QSpray' -> String
+ Math.Algebra.Hspray: prettyQSprayX1X2X3 :: String -> QSpray -> String
+ Math.Algebra.Hspray: prettyQSprayX1X2X3' :: String -> QSpray' -> String
+ Math.Algebra.Hspray: prettyQSprayXYZ :: [String] -> QSpray -> String
+ Math.Algebra.Hspray: prettyQSprayXYZ' :: [String] -> QSpray' -> String
+ Math.Algebra.Hspray: prettySprayX1X2X3 :: Show a => String -> Spray a -> String
+ Math.Algebra.Hspray: prettySymbolicQSpray' :: String -> SymbolicQSpray -> String
+ Math.Algebra.Hspray: prettySymbolicQSprayX1X2X3 :: String -> String -> SymbolicQSpray -> String
+ Math.Algebra.Hspray: prettySymbolicQSprayXYZ :: String -> [String] -> SymbolicQSpray -> String
+ Math.Algebra.Hspray: prettySymbolicSpray' :: (Eq a, Show a, C a) => String -> SymbolicSpray a -> String
+ Math.Algebra.Hspray: prettySymbolicSprayX1X2X3 :: (Eq a, Show a, C a) => String -> String -> SymbolicSpray a -> String
+ Math.Algebra.Hspray: prettySymbolicSprayXYZ :: (Eq a, Show a, C a) => String -> [String] -> SymbolicSpray a -> String
+ Math.Algebra.Hspray: showNumSpray :: (Num a, Ord a) => ([Seq Int] -> [String]) -> (a -> String) -> Spray a -> String
+ Math.Algebra.Hspray: showQSpray :: ([Seq Int] -> [String]) -> QSpray -> String
+ Math.Algebra.Hspray: showQSpray' :: ([Seq Int] -> [String]) -> QSpray' -> String
+ Math.Algebra.Hspray: showSpray :: (a -> String) -> (String, String) -> ([Seq Int] -> [String]) -> Spray a -> String
+ Math.Algebra.Hspray: showSprayX1X2X3 :: (a -> String) -> (String, String) -> String -> Spray a -> String
+ Math.Algebra.Hspray: showSprayX1X2X3' :: (a -> String) -> String -> Spray a -> String
+ Math.Algebra.Hspray: showSprayXYZ :: (a -> String) -> (String, String) -> [String] -> Spray a -> String
+ Math.Algebra.Hspray: showSprayXYZ' :: (a -> String) -> [String] -> Spray a -> String
+ Math.Algebra.Hspray: type QSpray' = Spray Rational'
- Math.Algebra.Hspray: prettySpray :: (a -> String) -> String -> Spray a -> String
+ Math.Algebra.Hspray: prettySpray :: Show a => Spray a -> String
- Math.Algebra.Hspray: prettySpray'' :: (a -> String) -> Spray a -> String
+ Math.Algebra.Hspray: prettySpray'' :: Show a => String -> Spray a -> String
- Math.Algebra.Hspray: prettySprayXYZ :: Show a => Spray a -> String
+ Math.Algebra.Hspray: prettySprayXYZ :: Show a => [String] -> Spray a -> String
- Math.Algebra.Hspray: type QSpray = Spray Rational'
+ Math.Algebra.Hspray: type QSpray = Spray Rational
Files
- CHANGELOG.md +23/−4
- README.md +96/−50
- hspray.cabal +10/−1
- src/Math/Algebra/Hspray.hs +597/−155
- tests/Main.hs +111/−7
CHANGELOG.md view
@@ -101,8 +101,27 @@ * New function `psPolynomial` which computes the power sum polynomials. -* A particular type of sprays, namely `SymbolicSpray a`, has been introduced. The coefficients of these -sprays are ratios of univariate polynomials with `a` coefficients. There is a specialization +* A particular type of sprays, namely `SymbolicSpray a`, has been introduced. The coefficients of +these sprays are ratios of univariate polynomials with `a` coefficients. There is a specialization `SymbolicQSpray` for the case when `a` is a type of rational numbers. The necessary instances have -been defined and there is the function `prettySymbolic(Q)Spray` to display such sprays. There are also -some functions to perform evaluation of such sprays.+been defined and there is the function `prettySymbolic(Q)Spray` to display such sprays. There are +also some functions to perform evaluation of such sprays. + + +## 0.2.6.0 - 2024-04-15 + +* New function `collinearSprays` which checks whether two sprays are collinear. + +* The function `isPolynomialOf` threw an error when the number of variables in the spray +to be tested was less than the number of variables in the list of sprays. That is me who +programmed this error and this was wrong: for example, `x = p1 - p2^*^p3` with `p1 = x + y^*^z`, +`p2 = y`, and `p3 = z`. + +* New functions to print sprays with numeric coefficients, such as `prettyNumSpray` and +`prettyQSpray`. + +* The functions `prettySpray`, `prettySpray'` and `prettySpray''` have been changed. + +* New functions to print symbolic sprays. + +* Documentation and README have been improved.
README.md view
@@ -9,6 +9,8 @@ ___ +The `Spray a` type represents the multivariate polynomials with coefficients +in `a`. For example: ```haskell import Math.Algebra.Hspray @@ -16,10 +18,32 @@ y = lone 2 :: Spray Double z = lone 3 :: Spray Double poly = (2 *^ (x^**^3 ^*^ y ^*^ z) ^+^ x^**^2) ^*^ (4 *^ (x ^*^ y ^*^ z)) -prettySpray show "X" poly --- "(4.0) * X^(3, 1, 1) + (8.0) * X^(4, 2, 2)" +putStrLn $ prettyNumSpray poly +-- 8.0*x^4.y^2.z^2 + 4.0*x^3.y.z ``` +This is the easiest way to construct a spray: first introduce the polynomial +variables with the `lone` function, and then use arithmetic operations. + +There are numerous functions to print a spray. If you don't like the letters +`x`, `y`, `z` in the output of `prettyNumSpray`, you can use `prettyNumSprayXYZ` +to change them to whatever you want: + +```haskell +putStrLn $ prettyNumSprayXYZ ["A","B","C"] poly +-- 8.0*A^4.B^2.C^2 + 4.0*A^3.B.C +``` + +Note that this function does not throw an error if you don't provide enough +letters: + +```haskell +putStrLn $ prettyNumSprayXYZ ["A","B"] poly +-- 8.0*A1^4.A2^2.A3^2 + 4.0*A1^3.A2.A3 +``` + +This is the same output as the one of `prettyNumSprayX1X2X3 "A" poly`. + More generally, one can use the type `Spray a` as long as the type `a` has the instances `Eq` and `Algebra.Ring` (defined in the **numeric-prelude** library). For example `a = Rational`: @@ -27,24 +51,24 @@ ```haskell import Math.Algebra.Hspray import Data.Ratio -x = lone 1 :: Spray Rational -y = lone 2 :: Spray Rational -z = lone 3 :: Spray Rational -poly = ((2%3) *^ (x^**^3 ^*^ y ^*^ z) ^+^ x^**^2) ^*^ ((7%4) *^ (x ^*^ y ^*^ z)) -prettySpray show "X" poly --- "(7 % 4) * X^(3, 1, 1) + (7 % 6) * X^(4, 2, 2)" +x = lone 1 :: QSpray -- QSpray = Spray Rational +y = lone 2 :: QSpray +z = lone 3 :: QSpray +poly = ((2%3) *^ (x^**^3 ^*^ y ^*^ z) ^-^ x^**^2) ^*^ ((7%4) *^ (x ^*^ y ^*^ z)) +putStrLn $ prettyQSpray poly +-- (7/6)*x^4.y^2.z^2 - (7/4)*x^3.y.z ``` Or `a = Spray Double`: ```haskell import Math.Algebra.Hspray -p = lone 1 :: Spray Double +alpha = lone 1 :: Spray Double x = lone 1 :: Spray (Spray Double) y = lone 2 :: Spray (Spray Double) -poly = ((p *^ x) ^+^ (p *^ y))^**^2 -prettySpray (prettySpray show "a") "X" poly --- "((1.0) * a^(2)) * X^(0, 2) + ((2.0) * a^(2)) * X^(1, 1) + ((1.0) * a^(2)) * X^(2)" +poly = ((alpha *^ x) ^+^ (alpha *^ y))^**^2 +showSprayXYZ' (prettyNumSprayXYZ ["alpha"]) ["x","y"] poly +-- (alpha^2)*x^2 + (2.0*alpha^2)*x.y + (alpha^2)*y^2 ``` #### Evaluation: @@ -69,13 +93,13 @@ x2 = lone 2 :: Spray Rational x3 = lone 3 :: Spray Rational poly = x1^**^2 ^+^ x2 ^+^ x3 ^-^ unitSpray -prettySpray' poly --- "((-1) % 1) + (1 % 1) x3 + (1 % 1) x2 + (1 % 1) x1^2" +putStrLn $ prettyQSprayX1X2X3 "x" poly +-- x1^2 + x2 + x3 - 1 -- -- substitute x1 -> 2 and x3 -> 3 -poly' = substituteSpray [Just 2, Nothing, Just 3] p -prettySpray' poly' --- "(6 % 1) + (1 % 1) x2" +poly' = substituteSpray [Just 2, Nothing, Just 3] poly +putStrLn $ prettyQSprayX1X2X3 "x" poly' +-- x2 + 6 ``` #### Differentiation: @@ -86,9 +110,12 @@ y = lone 2 :: Spray Double z = lone 3 :: Spray Double poly = 2 *^ (x ^*^ y ^*^ z) ^+^ (3 *^ x^**^2) --- derivate with respect to x -prettySpray show "X" $ derivSpray 1 poly --- "(2.0) * X^(0, 1, 1) + (6.0) * X^(1)" +putStrLn $ prettyNumSpray poly +-- 3.0*x^2 + 2.0*x.y.z +-- +-- derivative with respect to x +putStrLn $ prettyNumSpray $ derivSpray 1 poly +-- 6.0*x + 2.0*y.z" ``` ## Gröbner bases @@ -99,7 +126,7 @@ import Math.Algebra.Hspray import Data.Ratio -- define the elementary monomials -o = lone 0 :: Spray Rational +o = lone 0 :: Spray Rational -- same as unitSpray x = lone 1 :: Spray Rational y = lone 2 :: Spray Rational z = lone 3 :: Spray Rational @@ -110,12 +137,12 @@ -- compute the reduced Gröbner basis gbasis = groebner [p1, p2, p3] True -- show result -prettyResult = map prettySprayXYZ gbasis +prettyResult = map prettyQSpray gbasis mapM_ print prettyResult --- "((-1) % 1) + (1 % 1) Z^2 + (1 % 1) Y + (1 % 1) X" --- "(1 % 1) Z + ((-1) % 1) Z^2 + ((-1) % 1) Y + (1 % 1) Y^2" --- "((-1) % 2) Z^2 + (1 % 2) Z^4 + (1 % 1) YZ^2" --- "((-1) % 1) Z^2 + (4 % 1) Z^3 + ((-4) % 1) Z^4 + (1 % 1) Z^6" +-- "x + y + z^2 - 1" +-- "y^2 - y - z^2 + z" +-- "y.z^2 + (1/2)*z^4 - (1/2)*z^2" +-- "z^6 - 4*z^4 + 4*z^3 - z^2" ``` @@ -138,6 +165,7 @@ [Hackage page](https://hackage.haskell.org/package/numeric-prelude-0.4.4#usage) of **numeric-prelude**. + ## Symbolic coefficients Assume you have the polynomial `a * (x² + y²) + 2b/3 * z`, @@ -160,11 +188,10 @@ b = lone 2 :: Spray Rational poly = a *^ (x*x + y*y) + ((2%3) *^ b) *^ z -prettySpray (prettySpray show "a") "X" poly --- "((2 % 3) * a^(0, 1)) * X^(0, 0, 1) + ((1 % 1) * a^(1)) * X^(0, 2) + ((1 % 1) * a^(1)) * X^(2)" +putStrLn $ showSprayXYZ' (prettyQSprayXYZ ["a","b"]) ["X","Y","Z"] poly +-- (a)*X^2 + (a)*Y^2 + ((2/3)*b)*Z ``` -The `prettySpray` function shows the expansion of the polynomial. You can extract the powers and the coefficients as follows: ```haskell @@ -179,8 +206,13 @@ If you have only one symbolic coefficient, it is easier to deal with the sprays of type `SymbolicSpray`. These are sprays whose coefficients are ratios of univariate polynomials, -so this allows more possibilities than a `Spray (Spray a)`. -Assume you want to deal with the polynomial `4/5 * a/(a² + a + 1) * (x² + y²) + 2a/3 * yz`. +so this allows more possibilities than a `Spray (Spray a)`. Since the variable +of these univariate polynomials occurs in the coefficients of such a spray, I +call it the *outer variable* sometimes, although I do not very like this name +(see below). And I say that the variables of the symbolic spray are the +*inner variables* or the *main variables*, though I would prefer to simply call +them the *variables*. +Assume you want to deal with the polynomial `4/5 * a/(a² + 1) * (x² + y²) + 2a/3 * yz`. Then you define it as follows: ```haskell @@ -197,44 +229,58 @@ z = lone 3 :: SymbolicQSpray a = outerQVariable sSpray - = ((4%5) *. (a :% (a^2 + a + one))) *> (x^2 + y^2) + (constQPoly (2%3) * a) *> (y * z) -putStrLn $ prettySymbolicQSpray "a" sSpray --- ([(4/5)a] / [(1) + a + a^2])*x1^2 + ([(4/5)a] / [(1) + a + a^2])*x2^2 + ((2/3)a)*x2x3 + = ((4%5) *. (a :% (a^2 + one))) *> (x^2 + y^2) + (constQPoly (2%3) * a) *> (y * z) +putStrLn $ prettySymbolicQSpray' "a" sSpray +-- { [ (4/5)*a ] %//% [ a^2 + 1 ] }*X^2 + { [ (4/5)*a ] %//% [ a^2 + 1 ] }*Y^2 + { (2/3)*a }*Y.Z ``` -This pretty form of the symbolic qspray will be improved in a future version. - -There are three possible evaluations of this symbolic spray: +There are three possible evaluations of a symbolic spray: ```haskell -- substitute a value for 'a': -putStrLn $ prettySpray' $ evalSymbolicSpray sSpray (6%5) --- (24 % 91) x1^2 + (24 % 91) x2^2 + (4 % 5) x2x3 +putStrLn $ + prettyQSpray''' $ evalSymbolicSpray sSpray (6%5) +-- (24/61)*X^2 + (24/61)*Y^2 + (4/5)*Y.Z --- substitue a value for 'a' and some values for 'x1', 'x2', 'x3': +-- substitute a value for 'a' and some values for 'X', 'Y', 'Z': evalSymbolicSpray' sSpray (6%5) [2, 3, 4%7] --- 24 % 5 +-- 13848 % 2135 --- substitue some values for 'x1', 'x2', 'x3': +-- substitute some values for 'X', 'Y', 'Z': putStrLn $ prettyRatioOfQPolynomials "a" $ evalSymbolicSpray'' sSpray [2, 3, 4%7] --- [(404/35)a + (8/7)a^2 + (8/7)a^3] / [(1) + a + a^2] +-- [ (8/7)*a^3 + (404/35)*a ] %//% [ a^2 + 1 ] ``` -The nice point regarding these ratios of univariate polynomials is that they are automatically -"simplified". For example: +Although it does not make sense to replace the main variables (`X`, `Y`, `Z`) +of a symbolic spray with some fractions of univariate polynomials, this feature +is not provided. We rather consider that a `SymbolicSpray K` spray defines a +multivariate polynomial on the field `K` whose coefficients lie in `K` but +depend on a parameter, the so-called outer variable (`"a"`). By the way I am not +a fan of this name, and maybe the *parameter* would be a better name? And then +*parametric spray* would be a better name than *symbolic spray*? Do not +hesitate to open a Github issue to leave some comments if you want! +The nice point regarding these ratios of univariate polynomials is that they +are automatically "simplified" (i.e. written as irreducible fractions). +For example: + ```haskell -polyFrac = (a^8 - one) :% (a - one) +polyFrac = (a^8 - one) ^/^ (a - one) putStrLn $ prettyRatioOfQPolynomials "a" polyFrac --- (1) + a + a^2 + a^3 + a^4 + a^5 + a^6 + a^7 +-- a^7 + a^6 + a^5 + a^4 + a^3 + a^2 + a + 1 ``` Maybe you prefer the fractional form, but it is nice to see that this ratio of -polynomials actually is a polynomial. +polynomials actually is a polynomial. +Note that I used `^/^` here and not `:%`. That's because `:%` does not simplify +the fraction, it just constructs a fraction with the given numerator and denominator. +Whenever an arithmetic operation is performed on a fraction, the result is always +simplified. So the `^/^` operator simply constructs a fraction with `:%` and then +it multiplies it by one to get the simplification. ## Other features -Resultant and subresultants of two polynomials, and greatest common divisor of two polynomials -with coefficients in a field.+Resultant and subresultants of two polynomials, and greatest common divisor of +two polynomials with coefficients in a field.
hspray.cabal view
@@ -1,5 +1,5 @@ name: hspray -version: 0.2.5.0 +version: 0.2.6.0 synopsis: Multivariate polynomials. 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 @@ -53,6 +53,15 @@ , numeric-prelude >= 0.4.4 && < 0.5 , hspray Default-Language: Haskell2010 + ghc-options: -Wall + -Wcompat + -Widentities + -Wincomplete-record-updates + -Wincomplete-uni-patterns + -Wmissing-export-lists + -Wmissing-home-modules + -Wpartial-fields + -Wredundant-constraints benchmark benchmarks type: exitcode-stdio-1.0
src/Math/Algebra/Hspray.hs view
@@ -18,10 +18,11 @@ module Math.Algebra.Hspray ( - -- * Types + -- * Main types Powers (..) , Spray , QSpray + , QSpray' , Monomial -- * Basic sprays , lone @@ -40,7 +41,28 @@ , prettySpray' , prettySpray'' , prettySprayXYZ - -- * Univariate polynomials + , prettySprayX1X2X3 + , showSpray + , showSprayXYZ + , showSprayXYZ' + , showSprayX1X2X3 + , showSprayX1X2X3' + , showNumSpray + , showQSpray + , showQSpray' + , prettyNumSprayX1X2X3 + , prettyQSprayX1X2X3 + , prettyQSprayX1X2X3' + , prettyNumSprayXYZ + , prettyQSprayXYZ + , prettyQSprayXYZ' + , prettyNumSpray + , prettyNumSpray' + , prettyQSpray + , prettyQSpray'' + , prettyQSpray' + , prettyQSpray''' + -- * Univariate polynomials and fractions of univariate polynomials , A (..) , Rational' , Q @@ -49,6 +71,7 @@ , RatioOfPolynomials , QPolynomial , RatioOfQPolynomials + , (^/^) , prettyRatioOfPolynomials , prettyRatioOfQPolynomials , (*.) @@ -59,11 +82,17 @@ , qpolyFromCoeffs , outerQVariable , evalRatioOfPolynomials - -- * Symbolic sprays (with univariate polynomials coefficients) + -- * Symbolic sprays , SymbolicSpray , SymbolicQSpray + , prettySymbolicSprayX1X2X3 + , prettySymbolicSprayXYZ , prettySymbolicSpray + , prettySymbolicSpray' + , prettySymbolicQSprayX1X2X3 + , prettySymbolicQSprayXYZ , prettySymbolicQSpray + , prettySymbolicQSpray' , simplifySymbolicSpray , evalSymbolicSpray , evalSymbolicSpray' @@ -71,6 +100,7 @@ -- * Queries on a spray , getCoefficient , getConstantTerm + , numberOfVariables , sprayTerms -- * Evaluation of a spray , evalSpray @@ -106,6 +136,7 @@ , leadingTerm , isPolynomialOf , bombieriSpray + , collinearSprays ) where import qualified Algebra.Additive as AlgAdd import qualified Algebra.Field as AlgField @@ -125,6 +156,7 @@ , elemIndices , nub , foldl1' + , uncons ) import Data.Matrix ( Matrix , fromLists @@ -137,6 +169,7 @@ , fromJust, fromMaybe ) import Data.Ord ( comparing ) +import qualified Data.Ratio as DR import qualified Data.Sequence as S import Data.Sequence ( (><) , Seq @@ -155,6 +188,7 @@ , unpack ) import qualified MathObj.Polynomial as MathPol +import Number.Ratio ( T ( (:%) ) ) import qualified Number.Ratio as NumberRatio -- import qualified Algebra.PrincipalIdealDomain as AlgPID -- import qualified Algebra.Units as AlgUnits @@ -179,6 +213,12 @@ type QPolynomial = Polynomial Rational' type RatioOfQPolynomials = RatioOfPolynomials Rational' +-- | Division of univariate polynomials; this is an application of `:%` +-- followed by a simplification of the obtained fraction of the two polynomials +(^/^) :: (Eq a, AlgField.C a) + => Polynomial a -> Polynomial a -> RatioOfPolynomials a +(^/^) pol1 pol2 = simplifyRatioOfPolynomials $ pol1 :% pol2 + instance (Eq a, AlgField.C a) => AlgZT.C (A a) where isZero :: A a -> Bool isZero (A r) = r == AlgAdd.zero @@ -208,8 +248,8 @@ polyFromCoeffs :: [a] -> Polynomial a polyFromCoeffs as = MathPol.fromCoeffs (map A as) --- | The variable of a univariate polynomial; it is called \"outer\" because this is the variable --- occuring in the polynomial coefficients of a `SymbolicSpray` +-- | The variable of a univariate polynomial; it is called \"outer\" because +-- this is the variable occuring in the polynomial coefficients of a `SymbolicSpray` outerVariable :: AlgRing.C a => Polynomial a outerVariable = polyFromCoeffs [AlgAdd.zero, AlgRing.one] @@ -217,6 +257,8 @@ -- -- >>> import Number.Ratio ( (%) ) -- >>> constQPoly (2 % 3) +-- +-- prop> constQPoly (2 % 3) == qpolyFromCoeffs [2 % 3] constQPoly :: Rational' -> QPolynomial constQPoly = constPoly @@ -227,14 +269,16 @@ qpolyFromCoeffs :: [Rational'] -> QPolynomial qpolyFromCoeffs = polyFromCoeffs --- | The variable of a univariate qpolynomial; it is called \"outer\" because this is the variable --- occuring in the polynomial coefficients of a `SymbolicQSpray` +-- | The variable of a univariate rational polynomial; it is called \"outer\" +-- because it is the variable occuring in the coefficients of a `SymbolicQSpray` +-- (but I do not like this name - see README) -- -- prop> outerQVariable == qpolyFromCoeffs [0, 1] outerQVariable :: QPolynomial outerQVariable = qpolyFromCoeffs [0, 1] --- show a ratio +{- +-- show a ratio, helper function showQ :: (Eq a, Num a, Show a) => NumberRatio.T a -> String showQ q = if d == 1 then show n @@ -242,20 +286,60 @@ where n = NumberRatio.numerator q d = NumberRatio.denominator q + -} +-- | identify a `Polynomial a` to a `Spray a`, in order to apply the show spray +-- functions to the univariate polynomials +polynomialToSpray :: forall a. (Eq a, AlgRing.C a) => Polynomial a -> Spray a +polynomialToSpray pol = AlgAdd.sum terms + where + coeffs = MathPol.coeffs pol + indices = findIndices (/= A AlgAdd.zero) coeffs + get :: A a -> a + get (A x) = x + terms = map (\i -> get (coeffs!!i) *^ (lone 1 ^**^ i)) indices + +-- helper function; it encloses a string between two given delimiters +bracify :: (String, String) -> String -> String +bracify (lbrace, rbrace) x = lbrace ++ x ++ rbrace + -- | helper function for prettyRatioOfPolynomials (and prettySymbolicSpray) +showRatioOfPolynomials :: forall a. (Eq a, AlgField.C a) + => (Spray a -> String) -> RatioOfPolynomials a -> String +showRatioOfPolynomials sprayShower polysRatio = + numeratorString ++ denominatorString + where + numerator = NumberRatio.numerator polysRatio + denominator = NumberRatio.denominator polysRatio + brackets = denominator /= MathPol.const (A AlgRing.one) + enclose = bracify ("[ ", " ]") + numeratorString = if brackets + then enclose (sprayShower (polynomialToSpray numerator)) + else sprayShower (polynomialToSpray numerator) + denominatorString = if not brackets + then "" + else " %//% " ++ enclose (sprayShower (polynomialToSpray denominator)) + +-- | Pretty form of a ratio of univariate polynomials with rational coefficients +prettyRatioOfQPolynomials + :: String -- ^ a string to denote the variable, e.g. @"a"@ + -> RatioOfQPolynomials + -> String +prettyRatioOfQPolynomials var = showRatioOfPolynomials (prettyQSprayXYZ' [var]) + +-- | helper function for prettyRatioOfPolynomials (and prettySymbolicSpray) showQpol :: forall a. (Eq a, AlgField.C a) => Polynomial a -> String -> (a -> String) -> Bool -> String showQpol pol variable showCoeff brackets = if brackets - then '[' : polyString ++ "]" + then "[ " ++ polyString ++ " ]" else polyString where showCoeff' :: Int -> A a -> String showCoeff' i (A coeff) = case i of - 0 -> '(' : showCoeff coeff ++ ")" + 0 -> (bracify ("(", ")") . showCoeff) coeff _ -> if coeff == AlgRing.one then "" - else '(' : showCoeff coeff ++ ")" + else (bracify ("(", ")") . showCoeff) coeff coeffs = MathPol.coeffs pol nonzeros = findIndices (/= A AlgAdd.zero) coeffs terms = map (pack . showTerm) nonzeros @@ -273,24 +357,26 @@ where denominator = NumberRatio.denominator polysRatio brackets = denominator /= MathPol.const (A AlgRing.one) - numeratorString = showQpol (NumberRatio.numerator polysRatio) var showCoeff brackets + numeratorString = + showQpol (NumberRatio.numerator polysRatio) var showCoeff brackets denominatorString = if not brackets then "" - else " / " ++ showQpol denominator var showCoeff True + else " %//% " ++ showQpol denominator var showCoeff True -- | Pretty form of a ratio of univariate polynomials prettyRatioOfPolynomials :: (Eq a, AlgField.C a, Show a) - => String -- ^ a string to denote the variable, e.g. @"a"@ + => String -- ^ string (usually a single letter) to denote the variable, e.g. @"a"@ -> RatioOfPolynomials a -> String prettyRatioOfPolynomials var = showQpolysRatio var show --- | Pretty form of a ratio of univariate qpolynomials -prettyRatioOfQPolynomials +{- -- | Pretty form of a ratio of univariate qpolynomials +prettyRatioOfQPolynomials' :: String -- ^ a string to denote the variable, e.g. @"a"@ -> RatioOfQPolynomials -> String -prettyRatioOfQPolynomials var = showQpolysRatio var showQ +prettyRatioOfQPolynomials' var = showQpolysRatio var showQ + -} -- | Evaluates a ratio of univariate polynomials evalRatioOfPolynomials :: AlgField.C a @@ -300,8 +386,10 @@ evalRatioOfPolynomials value polysRatio = resultNumerator AlgField./ resultDenominator where - A resultNumerator = MathPol.evaluate (NumberRatio.numerator polysRatio) (A value) - A resultDenominator = MathPol.evaluate (NumberRatio.denominator polysRatio) (A value) + A resultNumerator = + MathPol.evaluate (NumberRatio.numerator polysRatio) (A value) + A resultDenominator = + MathPol.evaluate (NumberRatio.denominator polysRatio) (A value) -- Symbolic sprays ------------------------------------------------------------ @@ -309,28 +397,107 @@ type SymbolicSpray a = Spray (RatioOfPolynomials a) type SymbolicQSpray = SymbolicSpray Rational' --- | Simplifies the coefficients (the ratio of univariate polynomials) of a +-- | simplifies a ratio of polynomials (simply by multiplying it by one) +simplifyRatioOfPolynomials :: + (Eq a, AlgField.C a) => RatioOfPolynomials a -> RatioOfPolynomials a +simplifyRatioOfPolynomials = (AlgRing.*) AlgRing.one + +-- | Simplifies the coefficients (the fractions of univariate polynomials) of a -- symbolic spray simplifySymbolicSpray :: (Eq a, AlgField.C a) => SymbolicSpray a -> SymbolicSpray a -simplifySymbolicSpray = HM.map (AlgAdd.+ AlgAdd.zero) +simplifySymbolicSpray = HM.map simplifyRatioOfPolynomials --- | Pretty form of a symbolic spray -prettySymbolicSpray +-- | Pretty form of a symbolic spray, using a string (typically a letter) +-- followed by an index to denote the variables +prettySymbolicSprayX1X2X3 :: (Eq a, Show a, AlgField.C a) - => String -- ^ a string to denote the outer variable of the spray, e.g. @"a"@ + => String -- ^ string to denote the outer variable of the spray, e.g. @"a"@ + -> String -- ^ typically a letter, to denote the non-indexed variables -> SymbolicSpray a -- ^ a symbolic spray; note that this function does not simplify it -> String -prettySymbolicSpray var = prettySpray'' (showQpolysRatio var show) +prettySymbolicSprayX1X2X3 a = showSprayX1X2X3 (prettyRatioOfPolynomials a) ("{ ", " }") --- | Pretty form of a symbolic qspray -prettySymbolicQSpray - :: String -- ^ a string to denote the outer variable of the spray, e.g. @"a"@ +-- | Pretty form of a symbolic spray, using some given strings (typically some +-- letters) to denote the variables if possible, i.e. if enough letters are +-- provided; otherwise this function behaves exactly like +-- @prettySymbolicQSprayX1X2X3 a@ where @a@ is the first provided letter +prettySymbolicSprayXYZ + :: (Eq a, Show a, AlgField.C a) + => String -- ^ string to denote the outer variable of the spray, e.g. @"a"@ + -> [String] -- ^ typically some letters, to denote the main variables + -> SymbolicSpray a -- ^ a symbolic spray; note that this function does not simplify it + -> String +prettySymbolicSprayXYZ a = showSprayXYZ (prettyRatioOfPolynomials a) ("{ ", " }") + +-- | Pretty form of a symbolic spray; see the definition below and see +-- `prettySymbolicSprayXYZ` +-- +-- prop> prettySymbolicSpray a spray == prettySymbolicSprayXYZ a ["x","y","z"] spray +prettySymbolicSpray + :: (Eq a, Show a, AlgField.C a) + => String -- ^ string to denote the outer variable of the spray, e.g. @"a"@ + -> SymbolicSpray a -- ^ a symbolic spray; note that this function does not simplify it + -> String +prettySymbolicSpray a = prettySymbolicSprayXYZ a ["x", "y", "z"] + +-- | Pretty form of a symbolic spray; see the definition below and see +-- `prettySymbolicSprayXYZ` +-- +-- prop> prettySymbolicSpray' a spray == prettySymbolicSprayXYZ a ["X","Y","Z"] spray +prettySymbolicSpray' + :: (Eq a, Show a, AlgField.C a) + => String -- ^ string to denote the outer variable of the spray, e.g. @"a"@ + -> SymbolicSpray a -- ^ a symbolic spray; note that this function does not simplify it + -> String +prettySymbolicSpray' a = prettySymbolicSprayXYZ a ["X", "Y", "Z"] + +-- | Pretty form of a symbolic rational spray, using a string (typically a letter) +-- followed by an index to denote the variables +prettySymbolicQSprayX1X2X3 + :: String -- ^ string to denote the outer variable of the spray, e.g. @"a"@ + -> String -- ^ string to denote the non-indexed variables of the spray -> SymbolicQSpray -- ^ a symbolic qspray; note that this function does not simplify it -> String -prettySymbolicQSpray var = prettySpray'' (showQpolysRatio var showQ) +prettySymbolicQSprayX1X2X3 a x = + showSpray (prettyRatioOfQPolynomials a) ("{ ", " }") (showMonomialsX1X2X3 x) --- | Substitutes a value to the outer variable of a symbolic spray +-- | Pretty form of a symbolic rational spray, using some given strings (typically some +-- letters) to denote the variables if possible, i.e. if enough letters are +-- provided; otherwise this function behaves exactly like +-- @prettySymbolicQSprayX1X2X3 a@ where @a@ is the first provided letter +prettySymbolicQSprayXYZ + :: String -- ^ string to denote the outer variable of the spray, e.g. @"a"@ + -> [String] -- ^ usually some letters, to denote the variables of the spray + -> SymbolicQSpray -- ^ a symbolic qspray; note that this function does not simplify it + -> String +prettySymbolicQSprayXYZ a letters = + showSpray (prettyRatioOfQPolynomials a) ("{ ", " }") (showMonomialsXYZ letters) + +-- | Pretty form of a symbolic rational spray, using @"x"@, @"y"@ and @"z"@ for the variables +-- if possible; i.e. if the spray does not have more than three variables, otherwise +-- @"x1"@, @"x2"@, ... are used to denote the variables +-- +-- prop> prettySymbolicQSpray a == prettySymbolicQSprayXYZ a ["x","y","z"] +prettySymbolicQSpray + :: String -- ^ string to denote the outer variable of the spray, e.g. @"a"@ + -> SymbolicQSpray -- ^ the symbolic qspray to be printed; note that this function does not simplify it + -> String +prettySymbolicQSpray a = prettySymbolicQSprayXYZ a ["x", "y", "z"] + +-- | Pretty form of a symbolic rational spray, using @"X"@, @"Y"@ and @"Z"@ for the variables +-- if possible; i.e. if the spray does not have more than three variables, otherwise +-- @"X1"@, @"X2"@, ... are used +-- +-- prop> prettySymbolicQSpray' a = prettySymbolicQSprayXYZ a ["X","Y","Z"] +prettySymbolicQSpray' + :: String -- ^ string to denote the outer variable of the spray, e.g. @"a"@ + -> SymbolicQSpray -- ^ the symbolic qsprayto be printed; note that this function does not simplify it + -> String +prettySymbolicQSpray' a = prettySymbolicQSprayXYZ a ["X", "Y", "Z"] + +-- | Substitutes a value to the outer variable of a symbolic spray +-- (the variable occuring in the coefficients) evalSymbolicSpray :: AlgField.C a => SymbolicSpray a -> a -> Spray a evalSymbolicSpray spray x = HM.map (evalRatioOfPolynomials x) spray @@ -345,7 +512,7 @@ then evalSpray (evalSymbolicSpray spray x) xs else error "evalSymbolicSpray': not enough values provided." --- helper function for evalSymbolicSpray'' +-- | helper function for evalSymbolicSpray'' evalSymbolicMonomial :: (Eq a, AlgField.C a) => [a] -> Monomial (RatioOfPolynomials a) -> RatioOfPolynomials a evalSymbolicMonomial xs (powers, coeff) = @@ -406,7 +573,8 @@ type Monomial a = (Powers, a) type Spray a = HashMap Powers a -type QSpray = Spray Rational' +type QSpray = Spray Rational +type QSpray' = Spray Rational' -- | addition of two sprays addSprays :: (AlgAdd.C a, Eq a) => Spray a -> Spray a -> Spray a @@ -484,11 +652,12 @@ -- | Power of a spray (^**^) :: (AlgRing.C a, Eq a) => Spray a -> Int -> Spray a (^**^) p n = if n >= 0 - then AlgRing.product (replicate n p) + then p AlgRing.^ fromIntegral n else error "(^**^): negative power of a spray is not allowed." infixr 7 *^ --- | Scale a spray by a scalar +-- | Scale a spray by a scalar; if you import the /Algebra.Module/ module +-- then it is the same operation as @(*>)@ from this module (*^) :: (AlgRing.C a, Eq a) => a -> Spray a -> Spray a (*^) lambda pol = lambda AlgMod.*> pol @@ -523,6 +692,13 @@ pows' = Powers expts' (nvariables pows) -- | Derivative of a spray +-- +-- >>> x :: lone 1 :: Spray Int +-- >>> y :: lone 2 :: Spray Int +-- >>> spray = 2*^x ^-^ 3*^y^**^8 +-- >>> spray' = derivSpray 1 spray +-- >>> putStrLn $ prettyNumSpray spray' +-- 2 derivSpray :: (AlgRing.C a, Eq a) => Int -- ^ index of the variable of differentiation (starting at 1) @@ -535,13 +711,15 @@ p' = HM.toList p monomials = [ derivMonomial i mp | mp <- p' ] --- | Spray corresponding to the basic monomial x_n +-- | The @n@-th polynomial variable @x_n@ as a spray; one usually builds a +-- spray by introducing these variables and combining them with the arithmetic +-- operations -- -- >>> x :: lone 1 :: Spray Int -- >>> y :: lone 2 :: Spray Int --- >>> p = 2*^x^**^2 ^-^ 3*^y --- >>> putStrLn $ prettySpray' p --- (2) x1^2 + (-3) x2 +-- >>> spray = 2*^x^**^2 ^-^ 3*^y +-- >>> putStrLn $ prettyNumSpray spray +-- 2*x^2 - 3*y -- -- prop> lone 0 == unitSpray lone :: AlgRing.C a => Int -> Spray a @@ -565,6 +743,10 @@ zeroSpray :: (Eq a, AlgAdd.C a) => Spray a zeroSpray = AlgAdd.zero +-- | whether the spray is zero +isZeroSpray :: Spray a -> Bool +isZeroSpray spray = HM.size spray == 0 + -- | Constant spray -- -- prop> constantSpray 3 == 3 *^ unitSpray @@ -646,8 +828,8 @@ -- >>> x3 :: lone 3 :: Spray Int -- >>> p = x1^**^2 ^-^ x2 ^+^ x3 ^-^ unitSpray -- >>> p' = substituteSpray [Just 2, Nothing, Just 3] p --- >>> putStrLn $ prettySpray' p' --- (-1) x2 + (6) +-- >>> putStrLn $ prettyNumSpray p' +-- -x2 + 6 substituteSpray :: (Eq a, AlgRing.C a) => [Maybe a] -> Spray a -> Spray a substituteSpray subs spray = if length subs == n then spray' @@ -671,8 +853,8 @@ -- >>> z :: lone 3 :: Spray Int -- >>> p = x ^+^ y -- >>> q = composeSpray p [z, x ^+^ y ^+^ z] --- >>> putStrLn $ prettySprayXYZ q --- (1) X + (1) Y + (2) Z +-- >>> putStrLn $ prettyNumSpray' q +-- X + Y + 2*Z composeSpray :: forall a. (AlgRing.C a, Eq a) => Spray a -> [Spray a] -> Spray a composeSpray p = evalSpray (identify p) @@ -714,9 +896,9 @@ powers' = map (\exps -> simplifyPowers (Powers exps n')) expnts' spray' = HM.fromList (zip powers' coeffs) --- | Swaps two variables of a spray +-- | Swaps two variables whithin a spray -- --- prop> swapVariables (1, 3) p == permuteVariables [3, 2, 1] p +-- prop> swapVariables (1, 3) spray == permuteVariables [3, 2, 1] spray swapVariables :: (Int, Int) -> Spray a -> Spray a swapVariables (i, j) spray = if i>=1 && j>=1 @@ -739,128 +921,381 @@ -- pretty stuff --------------------------------------------------------------- --- | prettyPowers "x" [0, 2, 1] = x^(0, 2, 1) -prettyPowers :: String -> [Int] -> Text -prettyPowers var pows = append (pack x) (cons '(' $ snoc string ')') - where - x = " " ++ var ++ "^" - string = intercalate (pack ", ") (map (pack . show) pows) +-- | Prints a spray; this function is exported for +-- possible usage in other packages +showSpray + :: (a -> String) -- ^ function mapping a coefficient to a string, typically 'show' + -> (String, String) -- ^ pair of braces to enclose the coefficients + -> ([Seq Int] -> [String]) -- ^ function mapping a list of exponents to a list of strings representing the monomials corresponding to these exponents + -> Spray a -- ^ the spray to be printed + -> String +showSpray showCoef braces showMonomials spray = + if isZeroSpray spray + then "0" + else unpack $ intercalate (pack " + ") stringTerms + where + terms = sortBy (flip compare `on` fexpts) (HM.toList spray) + fexpts term = exponents $ fst term + coeffs = map snd terms + powers = map (exponents . fst) terms + stringMonomials = showMonomials powers + stringTerms = zipWith f coeffs stringMonomials + f coeff smonomial + | smonomial == "" = pack scoeff' + | scoeff == "" = pack smonomial + | otherwise = pack $ scoeff' ++ "*" ++ smonomial + where + scoeff = showCoef coeff + scoeff' = bracify braces scoeff --- | Pretty form of a spray +-- | Prints a spray, with monomials shown as "x.z^2", and with +-- a user-defined showing function for the coefficients +showSprayXYZ + :: (a -> String) -- ^ function mapping a coefficient to a string, typically 'show' + -> (String, String) -- ^ used to enclose the coefficients, usually a pair of braces + -> [String] -- ^ strings, typically some letters, to print the variables + -> Spray a -- ^ the spray to be printed + -> String +showSprayXYZ showCoef braces letters spray = + if null letters + then error "showSprayXYZ: empty list of strings." + else showSpray showCoef braces (showMonomialsXYZ letters) spray + +-- | Prints a spray, with monomials shown as @"x.z^2"@, and with +-- a user-defined showing function for the coefficients; this is the same as +-- the function `showSprayXYZ` with the pair of braces @("(", ")")@ +showSprayXYZ' + :: (a -> String) -- ^ function mapping a coefficient to a string, typically 'show' + -> [String] -- ^ strings, typically some letters, to print the variables + -> Spray a -- ^ the spray to be printed + -> String +showSprayXYZ' showCoef = showSprayXYZ showCoef ("(", ")") + +-- | Pretty form of a spray with monomials displayed in the style of @"x.z^2"@; +-- you should rather use `prettyNumSprayXYZ` or `prettyQSprayXYZ` if your +-- coefficients are numeric -- --- >>> x :: lone 1 :: Spray Int --- >>> y :: lone 2 :: Spray Int --- >>> z :: lone 3 :: Spray Int +-- >>> x = lone 1 :: Spray Int +-- >>> y = lone 2 :: Spray Int +-- >>> z = lone 3 :: Spray Int -- >>> p = 2*^x ^+^ 3*^y^**^2 ^-^ 4*^z^**^3 --- >>> putStrLn $ prettySpray show "x" p --- (2) * x^(1) + (3) * x^(0, 2) + (-4) * x^(0, 0, 3) -prettySpray - :: (a -> String) -- ^ function mapping a coefficient to a string, typically 'show' - -> String -- ^ a string denoting the variable, e.g. \"x\" - -> Spray a -- ^ the spray +-- >>> putStrLn $ prettySprayXYZ ["X", "Y", "Z"] p +-- (2)*X + (3)*Y^2 + (-4)*Z^3 +-- >>> putStrLn $ prettySprayXYZ ["X", "Y"] p +-- (2)*X1 + (3)*X2^2 + (-4)*X3^3 +prettySprayXYZ :: (Show a) + => [String] -- ^ typically some letters, to print the variables + -> Spray a -- ^ the spray to be printed -> 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 - stringTerm term = append - (snoc (snoc (cons '(' $ snoc stringCoef ')') ' ') '*') - (prettyPowers var pows) - where - pows = DF.toList $ exponents (fst term) - stringCoef = pack $ prettyCoef (snd term) +prettySprayXYZ = showSprayXYZ' show + +-- | Pretty form of a spray, with monomials shown as "x1.x3^2", and with +-- a user-defined showing function for the coefficients +showSprayX1X2X3 + :: (a -> String) -- ^ function mapping a coefficient to a string, typically 'show' + -> (String, String) -- ^ used to enclose the coefficients + -> String -- ^ typically a letter, to print the non-indexed variables + -> Spray a -- ^ the spray to be printed + -> String +showSprayX1X2X3 showCoef braces letter = + showSpray showCoef braces (showMonomialsX1X2X3 letter) --- | prettyPowers' [0, 2, 1] = "x2^2x3" -prettyPowers' :: Seq Int -> Text -prettyPowers' pows = pack x1x2x3 - where - n = S.length pows - f i p - | p == 0 = "" - | p == 1 = "x" ++ show i - | otherwise = "x" ++ show i ++ "^" ++ show p - x1x2x3 = concatMap (\i -> f i (pows `index` (i-1))) [1 .. n] +-- | Pretty form of a spray, with monomials shown as "x1.x3^2", and with +-- a user-defined showing function for the coefficients; this is the same as +-- the function `showSprayX1X2X3` with the pair of braces @("(", ")")@ used to +-- enclose the coefficients +showSprayX1X2X3' + :: (a -> String) -- ^ function mapping a coefficient to a string, e.g. 'show' + -> String -- ^ typically a letter, to print the non-indexed variables + -> Spray a -- ^ the spray to be printed + -> String +showSprayX1X2X3' showCoef = showSprayX1X2X3 showCoef ("(", ")") --- | Pretty form of a spray, with monomials shown as "x1x3^2" +-- | Pretty form of a spray with monomials displayed in the style of @"x1.x3^2"@; +-- you should rather use `prettyNumSprayX1X2X3` or `prettyQSprayX1X2X3` if your +-- coefficients are numeric -- --- >>> x :: lone 1 :: Spray Int --- >>> y :: lone 2 :: Spray Int --- >>> z :: lone 3 :: Spray Int +-- >>> x = lone 1 :: Spray Int +-- >>> y = lone 2 :: Spray Int +-- >>> z = lone 3 :: Spray Int +-- >>> spray = 2*^x ^+^ 3*^y^**^2 ^-^ 4*^z^**^3 +-- >>> putStrLn $ prettySprayX1X2X3 "X" spray +-- (2)*X1 + (3)*X2^2 + (-4)*X3^3 +prettySprayX1X2X3 :: (Show a) + => String -- ^ typically a letter, to print the non-indexed variables + -> Spray a -- ^ the spray to be printed + -> String +prettySprayX1X2X3 = showSprayX1X2X3' show + +-- | Pretty form of a spray with monomials displayed in the style of @"x.z^2"@; +-- you should rather use `prettyNumSpray` or `prettyQSpray` if you deal with +-- sprays with numeric coefficients +-- +-- >>> x = lone 1 :: Spray Int +-- >>> y = lone 2 :: Spray Int +-- >>> z = lone 3 :: Spray Int -- >>> p = 2*^x ^+^ 3*^y^**^2 ^-^ 4*^z^**^3 +-- >>> putStrLn $ prettySpray p +-- (2)*x + (3)*y^2 + (-4)*z^3 +-- >>> putStrLn $ prettySpray (p ^+^ lone 4) +-- (2)*x1 + (3)*x2^2 + (-4)*x3^3 + x4 +-- +-- prop> prettySpray spray == prettySprayXYZ ["x", "y", "z"] spray +prettySpray :: (Show a) => Spray a -> String +prettySpray = prettySprayXYZ ["x", "y", "z"] + +-- | Pretty form of a spray, with monomials shown as @"x1.x3^2"@; use +-- `prettySprayX1X2X3` to change the letter (or `prettyNumSprayX1X2X3` +-- or `prettyQSprayX1X2X3` if the coefficients are numeric) +-- +-- >>> x = lone 1 :: Spray Int +-- >>> y = lone 2 :: Spray Int +-- >>> z = lone 3 :: Spray Int +-- >>> p = 2*^x ^+^ 3*^y^**^2 ^-^ 4*^z^**^3 -- >>> putStrLn $ prettySpray' p --- (2) x1 + (3) x2^2 + (-4) x3^3 +-- (2)*x1 + (3)*x2^2 + (-4)*x3^3 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 - 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 ')' - stringCoef'' = if constant then stringCoef' else snoc stringCoef' ' ' +prettySpray' = prettySprayX1X2X3 "x" --- | Pretty form of a spray, with monomials shown as "x1x3^2", and with --- a user-defined showing function for the coefficients +-- | showMonomialOld "x" [0, 2, 1] = x^(0, 2, 1) +showMonomialsOld :: String -> [Seq Int] -> [String] +showMonomialsOld var = map (showMonomialOld var) + where + showMonomialOld :: String -> Seq Int -> String + showMonomialOld a pows = + unpack $ append (pack x) (cons '(' $ snoc string ')') + where + x = a ++ "^" + string = intercalate (pack ", ") (map (pack . show) (DF.toList pows)) + +-- | Pretty form of a spray; you will probably prefer `prettySpray` or `prettySpray'` -- --- prop> prettySpray' p == prettySpray'' show p -prettySpray'' :: (a -> String) -> Spray a -> String -prettySpray'' showCoeff spray = unpack $ intercalate (pack " + ") terms +-- >>> x = lone 1 :: Spray Int +-- >>> y = lone 2 :: Spray Int +-- >>> z = lone 3 :: Spray Int +-- >>> p = 2*^x ^+^ 3*^y^**^2 ^-^ 4*^z^**^3 +-- >>> putStrLn $ prettySpray'' "x" p +-- (2)*x^(1) + (3)*x^(0, 2) + (-4)*x^(0, 0, 3) +prettySpray'' + :: Show a + => String -- ^ a string denoting the variables, e.g. \"x\" + -> Spray a -- ^ the spray + -> String +prettySpray'' var = showSpray show ("(", ")") (showMonomialsOld var) + +-- | Show a spray with numeric coefficients; this function is exported for +-- possible usage in other packages +showNumSpray :: (Num a, Ord a) + => ([Seq Int] -> [String]) -- ^ function mapping a list of monomial exponents to a list of strings representing the monomials + -> (a -> String) -- ^ function mapping a positive coefficient to a string + -> Spray a + -> String +showNumSpray showMonomials showCoeff spray = + if isZeroSpray spray + then "0" + else concat $ zipWith (++) stringSigns stringTerms + where + terms = sortBy (flip compare `on` (exponents . fst)) (HM.toList spray) + coeffs = map snd terms + (firstCoeff, otherCoeffs) = fromJust (uncons coeffs) + firstSign = if firstCoeff > 0 then "" else "-" + otherSigns = map (\x -> if x > 0 then " + " else " - ") otherCoeffs + stringSigns = firstSign : otherSigns + absCoeffs = map abs coeffs + powers = map (exponents . fst) terms + stringMonomials = showMonomials powers + stringTerms = zipWith f absCoeffs stringMonomials + f acoeff smonomial + | smonomial == "" = showCoeff acoeff + | scoeff == "" = smonomial + | otherwise = scoeff ++ "*" ++ smonomial + where + scoeff = if acoeff == 1 then "" else showCoeff acoeff + +-- | showMonomialX1X2X3 "X" [0, 2, 1] = "X2^2.X3" +showMonomialX1X2X3 :: String -> Seq Int -> Text +showMonomialX1X2X3 x pows = x1x2x3 where - 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.null pows - stringCoef = pack $ showCoeff (snd term) - stringCoef' = cons '(' $ snoc stringCoef ')' - stringCoef'' = if constant then stringCoef' else snoc stringCoef' '*' + f i p + | p == 0 = pack "" + | p == 1 = pack $ x ++ show i + | otherwise = pack $ x ++ show i ++ "^" ++ show p + indices = S.findIndicesL (/= 0) pows + x1x2x3 = + intercalate (pack ".") (map (\i -> f (i+1) (pows `index` i)) indices) --- | prettyPowersXYZ [1, 2, 1] = XY^2Z -prettyPowersXYZ :: Seq Int -> Text -prettyPowersXYZ pows = if n <= 3 - then pack xyz - else error "prettyPowersXYZ: there is more than three variables" +-- | showMonomialsX1X2X3 "X" [[0, 2, 1], [1, 2]] = ["X2^2.X3", "X1.X2"] +showMonomialsX1X2X3 :: String -> [Seq Int] -> [String] +showMonomialsX1X2X3 x = map (unpack . showMonomialX1X2X3 x) + +-- | showMonomialXYZ ["X", "Y", "Z"] 3 [1, 2, 1] = X.Y^2.Z +-- showMonomialXYZ ["X", "Y", "Z"] 3 [1, 2, 1, 2] = X1.X2^2.X3.X4^2 +showMonomialXYZ :: [String] -> Int -> Seq Int -> Text +showMonomialXYZ letters n pows = if n <= length letters + then xyz + else showMonomialX1X2X3 (letters !! 0) pows where - n = S.length pows - gpows = growSequence pows n 3 f letter p - | 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) - xyz = x ++ y ++ z + | p == 0 = pack "" + | p == 1 = pack letter + | otherwise = pack $ letter ++ "^" ++ show p + indices = S.findIndicesL (/= 0) pows + xyz = intercalate (pack ".") + (map (\i -> f (letters!!i) (pows `index` i)) indices) --- | Pretty form of a spray having at more three variables +-- | showMonomialsXYZ ["X", "Y", "Z"] [[0, 2, 1], [1, 2]] = ["Y^2.Z", "X.Y^2"] +showMonomialsXYZ :: [String] -> [Seq Int] -> [String] +showMonomialsXYZ letters powers = map (unpack . showMonomialXYZ letters n) powers + where + n = maximum (map S.length powers) + +-- | Pretty form of a spray with numeric coefficients, printing monomials as @"x1.x3^2"@ -- --- >>> x :: lone 1 :: Spray Int --- >>> y :: lone 2 :: Spray Int --- >>> z :: lone 3 :: Spray Int +-- >>> x = lone 1 :: Spray Int +-- >>> y = lone 2 :: Spray Int +-- >>> z = lone 3 :: Spray Int -- >>> p = 2*^x ^+^ 3*^y^**^2 ^-^ 4*^z^**^3 --- >>> putStrLn $ prettySprayXYZ p --- (2) X + (3) Y^2 + (-4) Z^3 -prettySprayXYZ :: (Show a) => Spray a -> String -prettySprayXYZ spray = unpack $ intercalate (pack " + ") terms - where - terms = map stringTerm (sortBy (flip compare `on` fexpts) (HM.toList spray)) - fexpts term = exponents $ fst term - stringTerm term = append stringCoef'' (prettyPowersXYZ pows) - where - pows = exponents (fst term) - constant = S.null pows - stringCoef = pack $ show (snd term) - stringCoef' = cons '(' $ snoc stringCoef ')' - stringCoef'' = if constant then stringCoef' else snoc stringCoef' ' ' +-- >>> putStrLn $ prettyNumSprayX1X2X3 "x" p +-- 2*x1 + 3*x2^2 - 4*x3^3 +prettyNumSprayX1X2X3 :: (Num a, Ord a, Show a) + => String -- ^ usually a letter such as @"x"@ to denote the non-indexed variables + -> Spray a + -> String +prettyNumSprayX1X2X3 x = showNumSpray (showMonomialsX1X2X3 x) show +-- | Pretty form of a spray with numeric coefficients, printing monomials as @"x.z^2"@ +-- if possible, i.e. if enough letters are provided, otherwise as @"x1.x3^2"@ +-- +-- >>> x = lone 1 :: Spray Int +-- >>> y = lone 2 :: Spray Int +-- >>> z = lone 3 :: Spray Int +-- >>> w = lone 4 :: Spray Int +-- >>> p = 2*^x ^+^ 3*^y^**^2 ^-^ 4*^z^**^3 +-- >>> putStrLn $ prettyNumSprayXYZ ["x","y","z"] p +-- 2*x + 3*y^2 - 4*z^3 +-- >>> putStrLn $ prettyNumSprayXYZ ["x","y","z"] (p ^+^ w) +-- 2*x1 + 3*x2^2 - 4*x3^3 + x4 +-- >>> putStrLn $ prettyNumSprayXYZ ["a","b","c"] (p ^+^ w) +-- 2*a1 + 3*a2^2 - 4*a3^3 + a4 +prettyNumSprayXYZ :: (Num a, Ord a, Show a) + => [String] -- ^ usually some letters, denoting the variables + -> Spray a + -> String +prettyNumSprayXYZ letters = showNumSpray (showMonomialsXYZ letters) show +-- | helper function for showQSpray +showRatio :: Rational -> String +showRatio q = if d == 1 + then show n + else "(" ++ show n ++ "/" ++ show d ++ ")" + where + n = DR.numerator q + d = DR.denominator q + +-- | helper function for showQSpray' +showRatio' :: (Eq a, Num a, Show a) => NumberRatio.T a -> String +showRatio' q = if d == 1 + then show n + else "(" ++ show n ++ "/" ++ show d ++ ")" + where + n = NumberRatio.numerator q + d = NumberRatio.denominator q + +-- | Prints a `QSpray`; for internal usage but exported for usage in other packages +showQSpray :: + ([Seq Int] -> [String]) -- ^ function printing monomials + -> QSpray + -> String +showQSpray showMonomials = showNumSpray showMonomials showRatio + +-- | Prints a `QSpray'`; for internal usage but exported for usage in other packages +showQSpray' :: + ([Seq Int] -> [String]) -- ^ function mapping a list of monomials exponents to a list of strings + -> QSpray' + -> String +showQSpray' showMonomials = showNumSpray showMonomials showRatio' + +-- | Pretty form of a spray with rational coefficients, printing monomials in +-- the style of @"x1.x3^2"@ +-- +-- >>> x = lone 1 :: QSpray +-- >>> y = lone 2 :: QSpray +-- >>> z = lone 3 :: QSpray +-- >>> p = 2*^x ^+^ 3*^y^**^2 ^-^ (4%3)*^z^**^3 +-- >>> putStrLn $ prettyQSprayX1X2X3 "x" p +-- 2*x1 + 3*x2^2 - (4/3)*x3^3 +prettyQSprayX1X2X3 :: + String -- ^ usually a letter such as @"x"@, to denote the non-indexed variables + -> QSpray + -> String +prettyQSprayX1X2X3 x = showQSpray (showMonomialsX1X2X3 x) + +-- | Same as `prettyQSprayX1X2X3` but for a `QSpray'` spray +prettyQSprayX1X2X3' :: + String -- ^ usually a letter such as @"x"@, to denote the non-indexed variables + -> QSpray' + -> String +prettyQSprayX1X2X3' x = showQSpray' (showMonomialsX1X2X3 x) + +-- | Pretty form of a spray with rational coefficients, printing monomials in +-- the style of @"x.z^2"@ with the provided letters if possible, i.e. if enough +-- letters are provided, otherwise in the style @"x1.x3^2"@, taking the first +-- provided letter to denote the non-indexed variables +-- +-- >>> x = lone 1 :: QSpray +-- >>> y = lone 2 :: QSpray +-- >>> z = lone 3 :: QSpray +-- >>> p = 2*^x ^+^ 3*^y^**^2 ^-^ (4%3)*^z^**^3 +-- >>> putStrLn $ prettyQSprayXYZ ["x","y","z"] p +-- 2*x + 3*y^2 - (4/3)*z^3 +-- >>> putStrLn $ prettyQSprayXYZ ["x","y"] p +-- 2*x1 + 3*x2^2 - (4%3)*x3^3 +-- >>> putStrLn $ prettyQSprayXYZ ["a","b"] p +-- 2*a1 + 3*a2^2 - (4/3)*a3^3 +prettyQSprayXYZ :: + [String] -- ^ usually some letters, to denote the variables + -> QSpray + -> String +prettyQSprayXYZ letters = showQSpray (showMonomialsXYZ letters) + +-- | Same as `prettyQSprayXYZ` but for a `QSpray'` spray +prettyQSprayXYZ' :: + [String] -- ^ usually some letters, to denote the variables + -> QSpray' + -> String +prettyQSprayXYZ' letters = showQSpray' (showMonomialsXYZ letters) + +-- | Pretty printing of a spray with rational coefficients +-- prop> prettyQSpray == prettyQSprayXYZ ["x", "y", "z"] +prettyQSpray :: QSpray -> String +prettyQSpray = prettyQSprayXYZ ["x", "y", "z"] + +-- | Pretty printing of a spray with rational coefficients +-- prop> prettyQSpray'' == prettyQSprayXYZ ["X", "Y", "Z"] +prettyQSpray'' :: QSpray -> String +prettyQSpray'' = prettyQSprayXYZ ["X", "Y", "Z"] + +-- | Pretty printing of a spray with rational coefficients +-- prop> prettyQSpray' == prettyQSprayXYZ' ["x", "y", "z"] +prettyQSpray' :: QSpray' -> String +prettyQSpray' = prettyQSprayXYZ' ["x", "y", "z"] + +-- | Pretty printing of a spray with rational coefficients +-- prop> prettyQSpray''' == prettyQSprayXYZ' ["X", "Y", "Z"] +prettyQSpray''' :: QSpray' -> String +prettyQSpray''' = prettyQSprayXYZ' ["X", "Y", "Z"] + +-- | Pretty printing of a spray with numeric coefficients +-- prop> prettyNumSpray == prettyNumSprayXYZ ["x", "y", "z"] +prettyNumSpray :: (Num a, Ord a, Show a) => Spray a -> String +prettyNumSpray = prettyNumSprayXYZ ["x", "y", "z"] + +-- | Pretty printing of a spray with numeric coefficients +-- prop> prettyNumSpray' == prettyNumSprayXYZ ["X", "Y", "Z"] +prettyNumSpray' :: (Num a, Ord a, Show a) => Spray a -> String +prettyNumSpray' = prettyNumSprayXYZ ["X", "Y", "Z"] + + -- misc ----------------------------------------------------------------------- -- | Terms of a spray @@ -880,7 +1315,13 @@ factorial n = product [1 .. n] times k x = AlgAdd.sum (replicate k x) +-- | Whether two sprays are equal up to a scalar factor +collinearSprays :: (Eq a, AlgField.C a) => Spray a -> Spray a -> Bool +collinearSprays spray1 spray2 = r *^ spray2 == spray1 + where + r = snd (leadingTerm spray1) AlgField./ snd (leadingTerm spray2) + -- division stuff ------------------------------------------------------------- -- | index of the maximum of a list @@ -1111,7 +1552,7 @@ where rest = [ngbasis !! k | k <- [0 .. n-1] \\ [i]] --- | Groebner basis (always minimal and possibly reduced) +-- | Gröbner basis, always minimal and possibly reduced -- -- prop> groebner sprays True == reduceGroebnerBasis (groebner sprays False) groebner @@ -1165,7 +1606,7 @@ -- | Elementary symmetric polynomial -- -- >>> putStrLn $ prettySpray' (esPolynomial 3 2) --- (1) x1x2 + (1) x1x3 + (1) x2x3 +-- (1)*x1x2 + (1)*x1x3 + (1)*x2x3 esPolynomial :: (AlgRing.C a, Eq a) => Int -- ^ number of variables @@ -1200,7 +1641,9 @@ where expts = S.replicate (j-1) 0 |> k --- | Whether a spray is a symmetric polynomial +-- | Whether a spray is a symmetric polynomial, an inefficient algorithm +-- (use the function with the same name in the /jackpolynomials/ package +-- if you need efficiency) isSymmetricSpray :: forall a. (AlgField.C a, Eq a) => Spray a -> Bool isSymmetricSpray spray = check1 && check2 where @@ -1208,7 +1651,7 @@ indices = [1 .. n] gPolys = map (\i -> esPolynomial n i ^-^ lone (n + i)) indices gbasis = groebner0 gPolys - spray' = spray ^-^ (constantSpray (getConstantTerm spray)) + spray' = spray ^-^ constantSpray (getConstantTerm spray) g = sprayDivisionRemainder spray' gbasis gpowers = HM.keys g check1 = minimum (map nvariables gpowers) > n @@ -1230,11 +1673,10 @@ => Spray a -> [Spray a] -> (Bool, Maybe (Spray a)) isPolynomialOf spray sprays = result where - n = numberOfVariables spray - n' = maximum $ map numberOfVariables sprays + nov = numberOfVariables spray + n = maximum $ map numberOfVariables sprays result - | n > n' = (False, Nothing) - | n < n' = error "isPolynomialOf: not enough variables in the spray." + | nov > n = (False, Nothing) | otherwise = (checks, poly) where m = length sprays @@ -1300,7 +1742,7 @@ times :: a -> a -> a times x y = if x == AlgAdd.zero then AlgAdd.zero else x AlgRing.* y --- | the coefficients of a spray as a univariate spray in x with +-- | the coefficients of a spray as a univariate spray in x_1 with -- spray coefficients sprayCoefficients :: (Eq a, AlgRing.C a) => Spray a -> [Spray a] sprayCoefficients spray = @@ -1564,7 +2006,7 @@ leadingCoeff = foldl1' (^+^) (zipWith (curry fromMonomial) powers'' coeffs'') --- | pseudo-division of two sprays, assuming degA >= degB >= 0 +-- | Pseudo-division of two sprays, assuming degA >= degB >= 0 pseudoDivision :: (Eq a, AlgRing.C a) => Int -- ^ number of variables -> Spray a -- ^ A
tests/Main.hs view
@@ -1,5 +1,6 @@-module Main where -import qualified Algebra.Additive as AlgAdd +module Main (main) where +import qualified Algebra.Additive as AlgAdd +import qualified Algebra.Module as AlgMod import qualified Algebra.Ring as AlgRing import Approx ( assertApproxEqual ) import Data.Maybe ( fromJust ) @@ -24,6 +25,7 @@ fromList, toList, bombieriSpray, + collinearSprays, derivSpray, groebner, fromRationalSpray, @@ -38,14 +40,24 @@ subresultants1, sprayDivision, gcdSpray, - QSpray, + QSpray', SymbolicQSpray, evalRatioOfPolynomials, evalSymbolicSpray', qpolyFromCoeffs, - constQPoly, evalSymbolicSpray'' + constQPoly, + evalSymbolicSpray'', + prettyQSpray, + prettyQSprayX1X2X3, + prettySpray, + prettySpray'', + outerQVariable, + constQPoly, + prettySymbolicQSpray', + (*.) ) import Number.Ratio ( T ( (:%) ) ) +import qualified Number.Ratio as NR import Test.Tasty ( defaultMain , testGroup ) @@ -58,11 +70,22 @@ main = defaultMain $ testGroup "Testing hspray" - [ testCase "bombieriSpray" $ do + [ + testCase "collinearSprays" $ do let x = lone 1 :: Spray Rational y = lone 2 :: Spray Rational z = lone 3 :: Spray Rational + spray1 = + (2 % 1) *^ ((2 % 1) *^ (x ^**^ 3 ^*^ y ^**^ 2)) ^+^ (4 % 1) *^ z ^+^ (5 % 1) *^ unitSpray + spray2 = 121 *^ spray1 + assertBool "" (collinearSprays spray1 spray2) + + , testCase "bombieriSpray" $ do + let + x = lone 1 :: Spray Rational + y = lone 2 :: Spray Rational + z = lone 3 :: Spray Rational poly = (2 % 1) *^ ((2 % 1) *^ (x ^**^ 3 ^*^ y ^**^ 2)) ^+^ (4 % 1) *^ z ^+^ (5 % 1) *^ unitSpray bpoly = @@ -158,6 +181,15 @@ p = p1 ^*^ p2 ^+^ unitSpray assertEqual "" (isPolynomialOf p [p1, p2]) (True, Just $ x ^*^ y ^+^ unitSpray), + testCase "isPolynomialOf - 2" $ do + let + x = lone 1 :: Spray Rational + y = lone 2 :: Spray Rational + z = lone 3 :: Spray Rational + assertEqual "" + (isPolynomialOf x [x ^+^ y^*^z, y, z]) + (True, Just $ x ^-^ y^*^z), + testCase "power sum polynomials" $ do let x = lone 1 :: Spray Rational @@ -345,7 +377,7 @@ g :: (Eq a, AlgRing.C a) => Spray a -> Spray a -> Spray a -> (a, a, a) -> (a, a) g px py pz (x, y, z) = (evalSpray f1 [x, y, AlgAdd.zero], evalSpray f2 [AlgAdd.zero, AlgAdd.zero, z]) where (f1, f2) = f px py pz - (r1, r2) = g (lone 1 :: QSpray) (lone 2) (lone 3) (2, 3, 4) + (r1, r2) = g (lone 1 :: QSpray') (lone 2) (lone 3) (2, 3, 4) r = evalRatioOfPolynomials 5 rop1 AlgRing.* r1 AlgAdd.+ evalRatioOfPolynomials 5 rop2 AlgRing.* r2 (f1', f2') = f (lone 1 :: SymbolicQSpray) (lone 2) (lone 3) symSpray = rop1 *^ f1' ^+^ rop2 *^ f2' @@ -353,6 +385,78 @@ rop1' = evalSymbolicSpray'' f1' [2, 3] rop2' = evalSymbolicSpray'' f2' [0, 0, 4] r'' = evalRatioOfPolynomials 5 (rop1 AlgRing.* rop1' AlgAdd.+ rop2 AlgRing.* rop2') - assertEqual "" (r, r') (r', r'') + assertEqual "" (r, r') (r', r''), + + testCase "pretty spray" $ do + let + x = lone 1 :: Spray Rational + y = lone 2 :: Spray Rational + z = lone 3 :: Spray Rational + p1 = ((2%3) *^ x^**^3) ^*^ y ^-^ x^**^2 ^+^ y ^*^ z ^-^ (2%3) *^ unitSpray + p2 = (3%2) *^ p1 + p3 = AlgAdd.negate $ + swapVariables (1, 3) $ + p2 ^+^ unitSpray ^-^ (x^**^3 ^*^ y ^-^ ((3%2) *^ x^**^2)) + strings = + [ + prettyQSpray (zeroSpray ^*^ p1) + , prettyQSpray p1 + , prettyQSpray (AlgAdd.negate p2) + , prettyQSpray (p2 ^+^ lone 4) + , prettyQSpray p3 + , " ---------- " + , prettyQSprayX1X2X3 "a" (zeroSpray ^*^ p1) + , prettyQSprayX1X2X3 "a" p1 + , prettyQSprayX1X2X3 "a" (p2 ^+^ lone 4) + , prettyQSprayX1X2X3 "a" p3 + , " ---------- " + , prettySpray (zeroSpray ^*^ p1) + , prettySpray p1 + , prettySpray (p2 ^+^ lone 4) + , prettySpray p3 + , " ---------- " + , prettySpray'' "w" (zeroSpray ^*^ p1) + , prettySpray'' "w" p1 + , prettySpray'' "w" (p2 ^+^ lone 4) + , prettySpray'' "w" p3 + ] + strings' = + [ + "0" + , "(2/3)*x^3.y - x^2 + y.z - (2/3)" + , "-x^3.y + (3/2)*x^2 - (3/2)*y.z + 1" + , "x1^3.x2 - (3/2)*x1^2 + (3/2)*x2.x3 + x4 - 1" + , "-(3/2)*x.y" + , " ---------- " + , "0" + , "(2/3)*a1^3.a2 - a1^2 + a2.a3 - (2/3)" + , "a1^3.a2 - (3/2)*a1^2 + (3/2)*a2.a3 + a4 - 1" + , "-(3/2)*a1.a2" + , " ---------- " + , "0" + , "(2 % 3)*x^3.y + ((-1) % 1)*x^2 + (1 % 1)*y.z + ((-2) % 3)" + , "(1 % 1)*x1^3.x2 + ((-3) % 2)*x1^2 + (3 % 2)*x2.x3 + (1 % 1)*x4 + ((-1) % 1)" + , "((-3) % 2)*x.y" + , " ---------- " + , "0" + , "(2 % 3)*w^(3, 1) + ((-1) % 1)*w^(2) + (1 % 1)*w^(0, 1, 1) + ((-2) % 3)*w^()" + , "(1 % 1)*w^(3, 1) + ((-3) % 2)*w^(2) + (3 % 2)*w^(0, 1, 1) + (1 % 1)*w^(0, 0, 0, 1) + ((-1) % 1)*w^()" + , "((-3) % 2)*w^(1, 1)" + ] + assertEqual "" strings strings', + + testCase "prettySymbolicQSpray'" $ do + let + x = lone 1 :: SymbolicQSpray + y = lone 2 :: SymbolicQSpray + z = lone 3 :: SymbolicQSpray + a = outerQVariable + sSpray + = ((4 NR.% 5) *. (a :% (a AlgRing.^ 2 AlgAdd.+ AlgRing.one))) AlgMod.*> (x^**^2 ^-^ y^**^2) + ^+^ (constQPoly (2 NR.% 3) AlgRing.* a) AlgMod.*> (y ^*^ z) + string = prettySymbolicQSpray' "a" sSpray + string' = + "{ [ (4/5)*a ] %//% [ a^2 + 1 ] }*X^2 + { [ -(4/5)*a ] %//% [ a^2 + 1 ] }*Y^2 + { (2/3)*a }*Y.Z" + assertEqual "" string string' ]