packages feed

hspray 0.2.7.0 → 0.3.0.0

raw patch · 5 files changed

+1024/−414 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Math.Algebra.Hspray: evalSymbolicSpray :: (Eq a, C a) => SymbolicSpray a -> a -> Spray a
- Math.Algebra.Hspray: evalSymbolicSpray' :: (Eq a, C a) => SymbolicSpray a -> a -> [a] -> a
- Math.Algebra.Hspray: evalSymbolicSpray'' :: (Eq a, C a) => SymbolicSpray a -> [a] -> RatioOfPolynomials a
- Math.Algebra.Hspray: instance (GHC.Classes.Eq a, Algebra.Field.C a) => Algebra.Module.C (Math.Algebra.Hspray.Polynomial a) (Math.Algebra.Hspray.SymbolicSpray a)
- Math.Algebra.Hspray: outerQVariable :: QPolynomial
- Math.Algebra.Hspray: outerVariable :: C a => Polynomial a
- Math.Algebra.Hspray: prettySymbolicQSpray :: String -> SymbolicQSpray -> 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: 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: simplifySymbolicSpray :: (Eq a, C a) => SymbolicSpray a -> SymbolicSpray a
- Math.Algebra.Hspray: type SymbolicQSpray = SymbolicSpray Rational'
- Math.Algebra.Hspray: type SymbolicSpray a = Spray (RatioOfPolynomials a)
+ Math.Algebra.Hspray: (%:%) :: Spray a -> Spray a -> RatioOfSprays a
+ Math.Algebra.Hspray: -- a</tt> and <tt>RatioOfSprays a</tt>)
+ Math.Algebra.Hspray: asSimpleParametricSpray :: (Eq a, C a) => ParametricSpray a -> SimpleParametricSpray a
+ Math.Algebra.Hspray: asSimpleParametricSprayUnsafe :: ParametricSpray a -> SimpleParametricSpray a
+ Math.Algebra.Hspray: canCoerceToSimpleParametricSpray :: (Eq a, C a) => ParametricSpray a -> Bool
+ Math.Algebra.Hspray: changeParameters :: HasVariables b => Spray b -> [VariablesType b] -> Spray b
+ Math.Algebra.Hspray: changeVariables :: HasVariables b => b -> [VariablesType b] -> b
+ Math.Algebra.Hspray: evalOneParameterSpray :: (Eq a, C a) => OneParameterSpray a -> a -> Spray a
+ Math.Algebra.Hspray: evalOneParameterSpray' :: (Eq a, C a) => OneParameterSpray a -> a -> [a] -> a
+ Math.Algebra.Hspray: evalOneParameterSpray'' :: (Eq a, C a) => OneParameterSpray a -> [a] -> RatioOfPolynomials a
+ Math.Algebra.Hspray: evalParametricSpray :: (Eq b, C (BaseRing b) b, C b) => Spray b -> [BaseRing b] -> b
+ Math.Algebra.Hspray: evaluateAt :: HasVariables b => [BaseRing b] -> b -> BaseRing b
+ Math.Algebra.Hspray: fromOneParameterQSpray :: OneParameterQSpray -> ParametricQSpray
+ Math.Algebra.Hspray: fromOneParameterSpray :: (Eq a, C a) => OneParameterSpray a -> ParametricSpray a
+ Math.Algebra.Hspray: instance (GHC.Classes.Eq a, Algebra.Field.C a) => Algebra.Module.C (Math.Algebra.Hspray.Polynomial a) (Math.Algebra.Hspray.OneParameterSpray a)
+ Math.Algebra.Hspray: instance (GHC.Classes.Eq a, Algebra.Field.C a) => Algebra.Module.C (Math.Algebra.Hspray.Spray a) (Math.Algebra.Hspray.ParametricSpray a)
+ Math.Algebra.Hspray: instance (GHC.Classes.Eq a, Algebra.Field.C a) => Algebra.RightModule.C (Math.Algebra.Hspray.Spray a) (Math.Algebra.Hspray.ParametricSpray a)
+ Math.Algebra.Hspray: instance (GHC.Classes.Eq a, Algebra.Field.C a) => Math.Algebra.Hspray.HasVariables (Math.Algebra.Hspray.Polynomial a)
+ Math.Algebra.Hspray: instance (GHC.Classes.Eq a, Algebra.Field.C a) => Math.Algebra.Hspray.HasVariables (Math.Algebra.Hspray.RatioOfPolynomials a)
+ Math.Algebra.Hspray: instance (GHC.Classes.Eq a, Algebra.Ring.C a) => Algebra.Module.C a (Math.Algebra.Hspray.SimpleParametricSpray a)
+ Math.Algebra.Hspray: instance (GHC.Classes.Eq a, Algebra.Ring.C a) => Algebra.RightModule.C a (Math.Algebra.Hspray.SimpleParametricSpray a)
+ Math.Algebra.Hspray: numberOfParameters :: HasVariables b => Spray b -> Int
+ Math.Algebra.Hspray: parametricQSprayToOneParameterQSpray :: ParametricQSpray -> OneParameterQSpray
+ Math.Algebra.Hspray: parametricSprayToOneParameterSpray :: forall a. C a => ParametricSpray a -> OneParameterSpray a
+ Math.Algebra.Hspray: prettyOneParameterQSpray :: String -> OneParameterQSpray -> String
+ Math.Algebra.Hspray: prettyOneParameterQSpray' :: String -> OneParameterQSpray -> String
+ Math.Algebra.Hspray: prettyOneParameterQSprayX1X2X3 :: String -> String -> OneParameterQSpray -> String
+ Math.Algebra.Hspray: prettyOneParameterQSprayXYZ :: String -> [String] -> OneParameterQSpray -> String
+ Math.Algebra.Hspray: prettyOneParameterSpray :: (Eq a, Show a, C a) => String -> OneParameterSpray a -> String
+ Math.Algebra.Hspray: prettyOneParameterSpray' :: (Eq a, Show a, C a) => String -> OneParameterSpray a -> String
+ Math.Algebra.Hspray: prettyOneParameterSprayX1X2X3 :: (Eq a, Show a, C a) => String -> String -> OneParameterSpray a -> String
+ Math.Algebra.Hspray: prettyOneParameterSprayXYZ :: (Eq a, Show a, C a) => String -> [String] -> OneParameterSpray a -> String
+ Math.Algebra.Hspray: prettyParametricQSpray :: ParametricQSpray -> String
+ Math.Algebra.Hspray: prettyParametricQSprayABCXYZ :: [String] -> [String] -> ParametricQSpray -> String
+ Math.Algebra.Hspray: prettySimpleParametricQSpray :: SimpleParametricQSpray -> String
+ Math.Algebra.Hspray: prettySimpleParametricQSprayABCXYZ :: [String] -> [String] -> SimpleParametricQSpray -> String
+ Math.Algebra.Hspray: qsoleParameter :: QPolynomial
+ Math.Algebra.Hspray: soleParameter :: C a => Polynomial a
+ Math.Algebra.Hspray: substituteParameters :: (HasVariables b, Eq (BaseRing b), C (BaseRing b)) => Spray b -> [BaseRing b] -> Spray (BaseRing b)
+ Math.Algebra.Hspray: type BaseRing b;
+ Math.Algebra.Hspray: type OneParameterQSpray = OneParameterSpray Rational'
+ Math.Algebra.Hspray: type OneParameterSpray a = Spray (RatioOfPolynomials a)
+ Math.Algebra.Hspray: type SimpleParametricQSpray = SimpleParametricSpray Rational
+ Math.Algebra.Hspray: type SimpleParametricSpray a = Spray (Spray a)
- Math.Algebra.Hspray: (^/^) :: (Eq a, C a) => Polynomial a -> Polynomial a -> RatioOfPolynomials a
+ Math.Algebra.Hspray: (^/^) :: (Eq a, C a) => Spray a -> Spray a -> RatioOfSprays a
- Math.Algebra.Hspray: -- | The type of the objects the variables represent
+ Math.Algebra.Hspray: -- | The type of the variables (this is <tt>Spray a</tt> for both <tt>Spray
- Math.Algebra.Hspray: class HasVariables a where {
+ Math.Algebra.Hspray: class HasVariables b where {
- Math.Algebra.Hspray: derivative :: HasVariables a => Int -> a -> a
+ Math.Algebra.Hspray: derivative :: HasVariables b => Int -> b -> b
- Math.Algebra.Hspray: evaluate :: HasVariables a => a -> [VariablesType a] -> VariablesType a
+ Math.Algebra.Hspray: evaluate :: HasVariables b => b -> [BaseRing b] -> BaseRing b
- Math.Algebra.Hspray: gegenbauerPolynomial :: Int -> Spray (Spray Rational)
+ Math.Algebra.Hspray: gegenbauerPolynomial :: Int -> SimpleParametricQSpray
- Math.Algebra.Hspray: infixl 7 %//%
+ Math.Algebra.Hspray: infixl 7 %/%
- Math.Algebra.Hspray: isBivariate :: HasVariables a => a -> Bool
+ Math.Algebra.Hspray: isBivariate :: HasVariables b => b -> Bool
- Math.Algebra.Hspray: isConstant :: HasVariables a => a -> Bool
+ Math.Algebra.Hspray: isConstant :: HasVariables b => b -> Bool
- Math.Algebra.Hspray: isTrivariate :: HasVariables a => a -> Bool
+ Math.Algebra.Hspray: isTrivariate :: HasVariables b => b -> Bool
- Math.Algebra.Hspray: isUnivariate :: HasVariables a => a -> Bool
+ Math.Algebra.Hspray: isUnivariate :: HasVariables b => b -> Bool
- Math.Algebra.Hspray: jacobiPolynomial :: Int -> Spray RatioOfQSprays
+ Math.Algebra.Hspray: jacobiPolynomial :: Int -> ParametricQSpray
- Math.Algebra.Hspray: numberOfVariables :: HasVariables a => a -> Int
+ Math.Algebra.Hspray: numberOfVariables :: HasVariables b => b -> Int
- Math.Algebra.Hspray: permuteVariables :: HasVariables a => [Int] -> a -> a
+ Math.Algebra.Hspray: permuteVariables :: HasVariables b => [Int] -> b -> b
- Math.Algebra.Hspray: substitute :: HasVariables a => [Maybe (VariablesType a)] -> a -> a
+ Math.Algebra.Hspray: substitute :: HasVariables b => [Maybe (BaseRing b)] -> b -> b
- Math.Algebra.Hspray: swapVariables :: HasVariables a => (Int, Int) -> a -> a
+ Math.Algebra.Hspray: swapVariables :: HasVariables b => (Int, Int) -> b -> b
- Math.Algebra.Hspray: type VariablesType a;
+ Math.Algebra.Hspray: type VariablesType b;

Files

CHANGELOG.md view
@@ -169,7 +169,7 @@ `Spray (RatioOfSprays a)`. By the way, this type has been named 
 `ParametricSpray a`, but this is possibly temporary.
 
-* The class `HasVariables` has been introduced in order to have some functions
+* The class `HasVariables a` has been introduced in order to have some functions
 which apply to both `Spray a` objects and `RatioOfSprays a` objects.
 
 * The function `derivSpray` no longer exists. To get a derivative of a spray, 
@@ -179,3 +179,41 @@ * A spray with coefficients in a field can now be divided by a scalar by using
 the `/>` operator. This operator can also be used to divide a ratio of sprays 
 (with coefficients in a field) by a scalar.
+
+
+## 0.3.0.0 - 2024-04-21
+
+* The type `SymbolicSpray a` has been renamed to `OneParameterSpray a`, and 
+all functions names which contained the string `Symbolic` have been changed by
+replacing `Symbolic` with `OnePerameter`.
+
+* The class `HasVariables`, which is instantiated for `Spray` and 
+`RatioOfSprays`, has a new method `changeVariables` allowing to perform 
+polynomial transformations of the variables of a spray and of a ratio of 
+sprays. For sprays, this is the same as the `composeSpray` function.
+
+* The class `HasVariables` is now also instantiated for `RatioOfPolynomials`.
+
+* The type alias `ParametricSpray a = Spray (RatioOfSprays a)` has been kept 
+and the type alias `SimpleParametricSpray a = Spray (Spray a)` has been 
+introduced. We say that a `Spray b` spray is parametric when `b` has the 
+`HasVariables` instance. So this applies to a `ParametricSpray a` spray, to 
+a `SimpleParametricSpray a` spray, and also to a `OneParameterSpray a` spray
+(recall that `OneParameterSpray a = Spray (RatioOfPolynomials a)`).
+
+* Functions to print `ParametricSpray` sprays and `SimpleParametricSpray` 
+sprays.
+
+* Function `numberOfParameters`, returning the number of parameters of a 
+parametric spray, that is to say the number of variables occurring in the 
+coefficients of this spray.
+
+* Function `changeParameters`, to perform polynomial transformations of the 
+parameters of a parametric spray.
+
+* Function `substituteParameters` to replace the parameters of a parametric 
+spray with some values. This is the same as `evalSpraySpray`(which will 
+probably disappear in the future).
+
+* Function `evalParametricSpray` to replace the variables of a parametric 
+spray with some values.
README.md view
@@ -5,12 +5,16 @@ [![Stack-nightly](https://github.com/stla/hspray/actions/workflows/Stack-nightly.yml/badge.svg)](https://github.com/stla/hspray/actions/workflows/Stack-nightly.yml)
 <!-- badges: end -->
 
-*Simple multivariate polynomials in Haskell.*
+***Simple multivariate polynomials in Haskell.*** 
+This package deals with multivariate polynomials over a commutative ring, 
+fractions of multivariate polynomials over a commutative field, and 
+multivariate polynomials with symbolic parameters in their coefficients.
 
-___
+____
 
+The main type provided by this package is `Spray a`. 
 An object of type `Spray a` represents a multivariate polynomial whose
-coefficients are represented by the type `a`. For example:
+coefficients are represented by the objects of type `a`. For example:
 
 ```haskell
 import Math.Algebra.Hspray
@@ -23,7 +27,8 @@ ```
 
 This is the easiest way to construct a spray: first introduce the polynomial 
-variables with the `lone` function, and then use arithmetic operations.
+variables with the `lone` function, and then combine them with 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` 
@@ -35,7 +40,8 @@ ```
 
 Note that this function does not throw an error if you don't provide enough 
-letters:
+letters; in such a situation, it takes the first given letter and it appends 
+it with the digit `i` to denote the `i`-th variable: 
 
 ```haskell
 putStrLn $ prettyNumSprayXYZ ["A","B"] poly
@@ -71,16 +77,20 @@ -- (alpha^2)*x^2 + (2.0*alpha^2)*x.y + (alpha^2)*y^2
 ```
 
-#### Evaluation:
+We will come back to these sprays of type `Spray (Spray a)`. They can 
+be used to represent parametric polynomials.
 
+
+#### Evaluation of a spray:
+
 ```haskell
 import Math.Algebra.Hspray
 x = lone 1 :: Spray Double
 y = lone 2 :: Spray Double
 z = lone 3 :: Spray Double
-poly = 2 *^ (x ^*^ y ^*^ z) 
--- evaluate poly at x=2, y=1, z=2
-evalSpray poly [2, 1, 2]
+spray = 2 *^ (x ^*^ y ^*^ z) 
+-- evaluate spray at x=2, y=1, z=2
+evalSpray spray [2, 1, 2]
 -- 8.0
 ```
 
@@ -92,35 +102,36 @@ x1 = lone 1 :: Spray Rational
 x2 = lone 2 :: Spray Rational
 x3 = lone 3 :: Spray Rational
-poly = x1^**^2 ^+^ x2 ^+^ x3 ^-^ unitSpray
-putStrLn $ prettyQSprayX1X2X3 "x" poly
+spray = x1^**^2 ^+^ x2 ^+^ x3 ^-^ unitSpray
+putStrLn $ prettyQSprayX1X2X3 "x" spray
 -- x1^2 + x2 + x3 - 1
 --
--- substitute x1 -> 2 and x3 -> 3
-poly' = substituteSpray [Just 2, Nothing, Just 3] poly
-putStrLn $ prettyQSprayX1X2X3 "x" poly'
+-- substitute x1 -> 2 and x3 -> 3, and don't substitute x2
+spray' = substituteSpray [Just 2, Nothing, Just 3] spray
+putStrLn $ prettyQSprayX1X2X3 "x" spray'
 -- x2 + 6
 ```
 
-#### Differentiation:
+#### Differentiation of a spray:
 
 ```haskell
 import Math.Algebra.Hspray
 x = lone 1 :: Spray Double
 y = lone 2 :: Spray Double
 z = lone 3 :: Spray Double
-poly = 2 *^ (x ^*^ y ^*^ z) ^+^ (3 *^ x^**^2)
-putStrLn $ prettyNumSpray poly
+spray = 2 *^ (x ^*^ y ^*^ z) ^+^ (3 *^ x^**^2)
+putStrLn $ prettyNumSpray spray
 -- 3.0*x^2 + 2.0*x.y.z
 --
 -- derivative with respect to x
-putStrLn $ prettyNumSpray $ derivative 1 poly
+putStrLn $ prettyNumSpray $ derivative 1 spray
 -- 6.0*x + 2.0*y.z"
 ```
 
 ## Gröbner bases
 
-As of version 2.0.0, it is possible to compute a Gröbner basis.
+As of version 2.0.0, it is possible to compute a Gröbner basis of the ideal 
+generated by a list of spray polynomials.
 
 ```haskell
 import Math.Algebra.Hspray
@@ -163,9 +174,9 @@ x = lone 1 :: QSpray 
 y = lone 2 :: QSpray 
 z = lone 3 :: QSpray
-poly  = ((2%3) *^ (x^**^3 ^*^ y ^*^ z) ^-^ x^**^2) ^*^ ((7%4) *^ (x ^*^ y ^*^ z))
-poly' = ((2%3) *^ (x^3 * y * z) - x^2) * ((7%4) *^ (x * y * z))
-poly == poly'
+spray  = ((2%3) *^ (x^**^3 ^*^ y ^*^ z) ^-^ x^**^2) ^*^ ((7%4) *^ (x ^*^ y ^*^ z))
+spray' = ((2%3) *^ (x^3 * y * z) - x^2) * ((7%4) *^ (x * y * z))
+spray == spray'
 -- True
 ```
 
@@ -177,7 +188,7 @@ of **numeric-prelude**.
 
 
-## Symbolic coefficients
+## Symbolic parameters in the coefficients
 
 Assume you have the polynomial `a * (x² + y²) + 2b/3 * z`, 
 where `a` and `b` are symbolic rational numbers. You can represent this 
@@ -196,16 +207,16 @@ a = lone 1 :: Spray Rational
 b = lone 2 :: Spray Rational
 
-poly = a *^ (x^2 + y^2) + ((2 *^ b) /^ 3) *^ z 
+spray = a *^ (x^2 + y^2) + ((2 *^ b) /^ 3) *^ z 
 putStrLn $ 
-  showSprayXYZ' (prettyQSprayXYZ ["a","b"]) ["X","Y","Z"] poly
+  showSprayXYZ' (prettyQSprayXYZ ["a","b"]) ["X","Y","Z"] spray
 -- (a)*X^2 + (a)*Y^2 + ((2/3)*b)*Z
 ```
 
 You can extract the powers and the coefficients as follows:
 
 ```haskell
-l = toList poly
+l = toList spray
 map fst l
 -- [[0,0,1],[2],[0,2]]
 map toList $ map snd l
@@ -213,14 +224,29 @@ ```
 
 These `Spray (Spray a)` sprays can be very useful. They represent polynomials 
-whose coefficients depend on some parameters, with a polynomial dependence. 
-For example, the coefficients of the 
+whose coefficients polynomially depend on some parameters. 
+Actually there is a type alias of `Spray (Spray a)` in **hspray**, namely 
+`SimpleParametricSpray a`, and there are some convenient functions to deal 
+with sprays of this type. There is also a type alias of 
+`SimpleParametricSpray Rational`, namely `SimpleParametricQSpray`.
+For example we can print our `SimpleParametricQSpray` spray `spray` as follows:
+
+```haskell
+putStrLn $ 
+  prettySimpleParametricQSprayABCXYZ ["a","b"] ["X","Y","Z"] spray
+-- { a }*X^2 + { a }*Y^2 + { (2/3)*b }*Z
+```
+
+The 
 [Gegenbauer polynomials](https://en.wikipedia.org/wiki/Gegenbauer_polynomials)
-are polynomials in their parameter $\alpha$ (this is clear from the recurrence 
-relation). Here is their implementation in **hspray**:
+are a real-life example of polynomials that can be represented by 
+`SimpleParametricQSpray` sprays. They are univariate polynomials whose 
+coefficients polynomially depend on a parameter $\alpha$ (the polynomial 
+dependency is clearly visible from the recurrence relation given on 
+Wikipedia). Here is their recursive implementation in **hspray**:
 
 ```haskell
-gegenbauerPolynomial :: Int -> Spray (Spray Rational) 
+gegenbauerPolynomial :: Int -> SimpleParametricQSpray 
 gegenbauerPolynomial n 
   | n == 0 = unitSpray
   | n == 1 = (2.^a) *^ x
@@ -228,8 +254,8 @@     (2.^(n'' ^+^ a) /^ n') *^ (x ^*^ gegenbauerPolynomial (n - 1))
     ^-^ ((n'' ^+^ 2.^a ^-^ unitSpray) /^ n') *^ gegenbauerPolynomial (n - 2)
   where 
-    x = lone 1 :: Spray (Spray Rational)
-    a = lone 1 :: Spray Rational
+    x = lone 1 :: SimpleParametricQSpray
+    a = lone 1 :: QSpray
     n'  = toRational n
     n'' = constantSpray (n' - 1)
 ```
@@ -240,143 +266,70 @@ n = 3
 g = gegenbauerPolynomial n
 putStrLn $ 
-  showSprayXYZ' (prettyQSprayXYZ ["alpha"]) ["X"] g
--- ((4/3)*alpha^3 + 4*alpha^2 + (8/3)*alpha)*X^3 + (-2*alpha^2 - 2*alpha)*X
+  prettySimpleParametricQSprayABCXYZ ["alpha"] ["X"]  g
+-- { (4/3)*alpha^3 + 4*alpha^2 + (8/3)*alpha }*X^3 + { -2*alpha^2 - 2*alpha }*X
 ```
 
-Let's check the differential equation:
+Let's check the differential equation given in the Wikipedia article:
 
 ```haskell
 g'  = derivative 1 g
 g'' = derivative 1 g'
-alpha = lone 1 :: Spray Rational
-x     = lone 1 :: Spray (Spray Rational)
+alpha = lone 1 :: QSpray
+x     = lone 1 :: SimpleParametricQSpray
 nAsSpray = constantSpray (toRational n)
 shouldBeZero = 
   (unitSpray ^-^ x^**^2) ^*^ g''
-  ^-^ (2.^alpha ^+^ unitSpray) *^ (x ^*^ g')
-  ^+^ n.^(nAsSpray ^+^ 2.^alpha) *^ g
+    ^-^ (2.^alpha ^+^ unitSpray) *^ (x ^*^ g')
+      ^+^ n.^(nAsSpray ^+^ 2.^alpha) *^ g
 putStrLn $ prettySpray shouldBeZero
 -- 0
 ```
 
-Now, how to substitute a value to the parameter $\alpha$? The package provides 
-the function `evalSpraySpray` to perform this task:
+Now, how to substitute a value to the parameter $\alpha$? For example, it is 
+said in the Wikipedia article that this yields the Legendre polynomials for 
+$\alpha = 1/2$. The package provides the function `substituteParameters` to 
+perform this task:
 
 ```haskell
+import Data.Ratio (%)
 putStrLn $ 
-  prettyQSpray'' $ evalSpraySpray g [1]
--- 8*X^3 - 4*X
+  prettyQSpray'' $ substituteParameters g [1%2]
+-- (5/2)*X^3 - (3/2)*X
 ```
 
 This is a `Spray Rational` spray.
 
-
-## The `SymbolicSpray` type
-
-If you have only one symbolic coefficient, you can deal with the sprays 
-of type `SymbolicSpray a`. These are sprays whose coefficients are 
-*ratios of univariate polynomials*, 
-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
-import           Prelude hiding ((*), (+), (-), (/), (^), (*>))
-import qualified Prelude as P
-import           Algebra.Additive              
-import           Algebra.Module            
-import           Algebra.Ring
-import           Algebra.Field
-import           Math.Algebra.Hspray
-import           Number.Ratio       ( (%), T ( (:%) ) )
-x = lone 1 :: SymbolicQSpray 
-y = lone 2 :: SymbolicQSpray 
-z = lone 3 :: SymbolicQSpray 
-a = outerQVariable  
-sSpray 
-  = ((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
-```
-
-There are three possible evaluations of a symbolic spray:
+The Wikipedia article also provides the value at $1$ of the Gegenbauer 
+polynomials in function of $\alpha$. We can get this value with 
+`evalParametricSpray`:
 
 ```haskell
--- substitute a value for 'a':
 putStrLn $ 
-  prettyQSpray''' $ evalSymbolicSpray sSpray (6%5)
--- (24/61)*X^2 + (24/61)*Y^2 + (4/5)*Y.Z
-
--- substitute a value for 'a' and some values for 'X', 'Y', 'Z':
-evalSymbolicSpray' sSpray (6%5) [2, 3, 4%7]
--- 13848 % 2135
-
--- substitute some values for 'X', 'Y', 'Z':
-putStrLn $ 
-  prettyRatioOfQPolynomials "a" $ evalSymbolicSpray'' sSpray [2, 3, 4%7]
--- [ (8/7)*a^3 + (404/35)*a ] %//% [ a^2 + 1 ]
-```
-
-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)
-putStrLn $ prettyRatioOfQPolynomials "a" polyFrac
--- a^7 + a^6 + a^5 + a^4 + a^3 + a^2 + a + 1
+  prettyQSprayXYZ ["alpha"] $ evalParametricSpray g [1]
+-- (4/3)*alpha^3 + 2*alpha^2 + (2/3)*alpha
 ```
 
-Maybe you prefer the fractional form, but it is nice to see that this ratio of 
-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.
+This is also a `Spray Rational` spray.
 
 
-## As of version 0.2.7: `RatioOfSprays`
-
-So far we have good stuff to deal with symbolic coefficients: the 
-`Spray (Spray a)` sprays and the `SymbolicSpray a` sprays. The 
-`SymbolicSpray a` sprays are successfully used in the 
-[**jackpolynomials** package](https://github.com/stla/jackpolynomials). 
-However this is not enough. For example we cannot implement the 
-[Jacobi polynomials](https://en.wikipedia.org/wiki/Jacobi_polynomials) 
-with symbolic parameters, because they have two parameters and their 
-recurrence relation involves some divisions of their coefficients. 
-
-We need a new type, similar to `SymbolicSpray a` but allowing multivariate 
-fractions of polynomials for the coefficients.
+## Ratios of sprays and general parametric sprays
 
-A first step in this direction has been achieved in version 0.2.7: the 
-type `RatioOfSprays a`, whose objects represent ratios of sprays, has 
-been introduced. Thus it suffices to introduce the type 
-`Spray (RatioOfSprays a)` now.
+Since you have just seen that the type `Spray (Spray a)` is named 
+`SimpleParametricSpray`, you probably guessed there is also a more general 
+type named `ParametricSpray`. Yes, and this is an alias of 
+`Spray (RatioOfSprays a)`, where the type `RatioOfSprays a` has not been 
+discussed yet. The objects of this type represent fractions of multivariate 
+polynomials and so this type is a considerable enlargment of the `Spray a` 
+type. Thus the `Spray (RatioOfSprays a)` sprays can represent multivariate 
+polynomials whose coefficients depend on some parameters, with a dependence 
+described by a fraction of polynomials in these parameters. Let's start with 
+a short presentation of the ratios of sprays.
 
-Thus the `Spray (RatioOfSprays a)` sprays are more general than the 
-`SymbolicSpray a` sprays, which are restricted to univariate fractions 
-of polynomials. But it is possible that the `Spray (RatioOfSprays a)` 
-sprays will be less efficient than the `SymbolicSpray a` sprays in the 
-univariate case. I will have to benchmark in order to get an answer to 
-this question.
+### The `RatioOfSprays` type
 
+The type `RatioOfSprays a`, whose objects represent ratios of sprays, has 
+been introduced in version 0.2.7.0. 
 To construct a ratio of sprays, apply `%//%` between its numerator and 
 its denominator:
 
@@ -389,13 +342,16 @@ -- [ 1 ] %//% [ x + y ]
 ```
 
-The `%//%` operator always returns an irreducible fraction.
+The `%//%` operator always returns an *irreducible fraction*. If you are 
+***sure*** that your numerator and your denominator are coprime, you can use
+the `%:%` instead, to gain some efficiency. But if they are not coprime, this 
+can have unfortunate consequences.
 
 The `RatioOfSprays a` type makes sense when `a` has a field instance, and then 
 it has a field instance too. To use the field operations, import the necessary
 modules from **numeric-prelude**, and hide these operations from the `Prelude`
-module (then you can also use the **numeric-prelude** operations for sprays, 
-instead of using `^+^`, `^-^`, `^*^`, `^**^`):
+module; then you can also use the **numeric-prelude** operations for sprays, 
+instead of using `^+^`, `^-^`, `^*^`, `^**^`:
 
 ```haskell
 import Prelude hiding ((+), (-), (*), (/), (^), (*>), (<*))
@@ -415,6 +371,8 @@ rOS = rOS1^2 + rOS1*rOS2 - rOS1/rOS2 + rOS2 -- slow!
 (rOS1 + rOS2) * (rOS1 - rOS2) == rOS1^2 - rOS2^2
 -- True
+rOS / rOS == unitRatioOfSprays
+-- True
 ```
 
 The `RatioOfSprays a` type also has left and right module instances over `a` 
@@ -424,8 +382,6 @@ ```haskell
 import Data.Ratio ( (%) )
 rOS' = (3%4::Rational) *> rOS^2  +  p *> rOS
-rOS' / rOS' == unitRatioOfSprays
--- True
 ```
 
 You can also divide a ratio of sprays by a spray with `%/%`:
@@ -460,6 +416,118 @@ evalRatioOfSprays rOS values == f r1 r2
 -- True
 ```
+
+### The `ParametricSpray` type
+
+Recall that `SimpleParametricSpray a = Spray (Spray a)` and 
+`ParametricSpray a = Spray (RatioOfSprays a)`, and we have the aliases 
+`SimpleParametricQSpray = SimpleParametricSpray Rational` and 
+`ParametricQSpray = ParametricSpray Rational`.
+
+The functions `substituteParameters` and `evalParametricSpray`, that we 
+previously applied to a `SimpleParametricSpray a` spray, are also applicable 
+to a `ParametricSpray a` spray. We didn't mention the function 
+`changeParameters` yet, which is also applicable to these two types of sprays.
+This function performs some polynomial transformations of the parameters of a
+parametric spray. 
+For example, consider the 
+[Jacobi polynomials](https://en.wikipedia.org/wiki/Jacobi_polynomials). 
+They are univariate polynomials with two parameters $\alpha$ and $\beta$. 
+They are implemented in **hspray** as `ParametricQSpray` sprays. In fact 
+it seems that the coefficients of the Jacobi polynomials *polynomially* 
+depend on $\alpha$ and $\beta$, and if this is true one could implement them 
+as `SimpleParametricQSpray` sprays. I will come back to this point later. The 
+recurrence relation defining the Jacobi polynomials involves a division which 
+makes the type `ParametricQSpray` necessary anyway. 
+The `changeParameters` function is useful to derive the Gegenbauer polynomials 
+from the Jacobi polynomials. Indeed, as asserted in the Wikipedia article, 
+the Gegenbauer polynomials coincide, up to a factor, with the Jacobi 
+polynomials with parameters $\alpha - 1/2$ and $\alpha - 1/2$. Here is how 
+to apply the `changeParameters` function to get this special case of Jacobi 
+polynomials:
+
+```haskell
+import Data.Ratio ( (%) )
+j = jacobiPolynomial 3
+alpha = qlone 1
+alpha' = alpha ^-^ constantSpray (1%2)
+j' = changeParameters j [alpha', alpha']
+```
+
+Now let's come back to the conjecture claiming that the coefficients of the 
+Jacobi polynomials *polynomially* depend on $\alpha$ and $\beta$, and thus 
+these polynomials can be represented by `SimpleParametricQSpray` sprays. 
+Maybe this can be deduced from a formula given in the Wikipedia article, I 
+didn't spend some time on this problem. I made this conjecture because I 
+observed this fact for some small values of $n$, and I tried the function
+`canCoerceToSimpleParametricSpray` for other values, which always returned 
+`True`. One can apply the function `asSimpleParametricSpray` to perform the 
+coercion.
+
+
+## The `OneParameterSpray` type
+
+Finally, let us mention the `OneParameterSpray a` type. Objects of this type 
+represent multivariate polynomials whose coefficients are fractions 
+of polynomials in only one parameter. So they are less general than the 
+`ParametricSpray a` sprays, but they are a bit more efficient.
+
+Assume for example that you want to deal with the polynomial 
+`4/5 * a/(a² + 1) * (x² + y²) + 2a/3 * yz`. 
+Then you define it as follows:
+
+```haskell
+import           Prelude hiding ((*), (+), (-), (/), (^), (*>))
+import qualified Prelude as P
+import           Algebra.Additive              
+import           Algebra.Module            
+import           Algebra.Ring
+import           Algebra.Field
+import           Math.Algebra.Hspray
+import           Number.Ratio       ( (%), T ( (:%) ) )
+x = lone 1 :: OneParameterQSpray 
+y = lone 2 :: OneParameterQSpray 
+z = lone 3 :: OneParameterQSpray
+a = qsoleParameter
+spray 
+  = ((4%5) *. (a :% (a^2 + one))) *> (x^2 + y^2)  +  (constQPoly (2%3) * a) *> (y * z)
+putStrLn $ prettyOneParameterQSpray' "a" spray
+-- { [ (4/5)*a ] %//% [ a^2 + 1 ] }*X^2 + { [ (4/5)*a ] %//% [ a^2 + 1 ] }*Y^2 + { (2/3)*a }*Y.Z
+```
+
+Not very easy... If you are more comfortable with the `ParametricSpray` sprays, 
+construct such a spray and convert it to a `OneParameterSpray` with the 
+function `parametricSprayToOneParameterSpray` or 
+`parametricQSprayToOneParameterQSpray`.
+
+The functions we have seen for the simple parametric sprays and the parametric 
+sprays are also applicable to the one-parameter sprays. These are sprays of 
+type `Spray (RatioOfPolynomials a)`, where the type `RatioOfPolynomials a` 
+deals with objects that represent fractions of *univariate* polynomials.
+
+Similary to the ratios of sprays, the nice point regarding these ratios of 
+univariate polynomials is that they are automatically written as irreducible 
+fractions. For example:
+
+```haskell
+polyFrac = (a^8 - one) % (a - one)
+putStrLn $ prettyRatioOfQPolynomials "a" polyFrac
+-- a^7 + a^6 + a^5 + a^4 + a^3 + a^2 + a + 1
+```
+
+Note that I used `%` here and not `:%`. That's because `:%` does not reduce 
+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 an irreducible fraction. 
+
+The `OneParameterSpray a` sprays are used in the 
+[**jackpolynomials** package](https://github.com/stla/jackpolynomials). 
+
+There is a slightly annoying point to note: the type `OneParameterQSpray` 
+is *not* `OneParameterSpray Rational`: it is `OneParameterSpray Rational'`, 
+where `Rational'` is a type similar to `Rational` defined in the 
+**numeric-prelude** package. I had to use this type because `Rational'` has 
+the necessary instances. 
 
 
 ## Other features
hspray.cabal view
@@ -1,7 +1,7 @@ name:                hspray
-version:             0.2.7.0
+version:             0.3.0.0
 synopsis:            Multivariate polynomials and fractions of multivariate polynomials.
-description:         Manipulation of multivariate polynomials over a commutative ring and fractions of multivariate polynomials over a commutative field, Gröbner bases, resultant and subresultants, and greatest common divisor.
+description:         Manipulation of multivariate polynomials over a commutative ring and fractions of multivariate polynomials over a commutative field, Gröbner bases, resultant and subresultants, and greatest common divisor. It is possible to deal with multivariate polynomials whose coefficients are fractions of multivariate polynomials, and they can be interpreted as parametric polynomials with symbolic parameters.
 homepage:            https://github.com/stla/hspray#readme
 license:             GPL-3
 license-file:        LICENSE
@@ -54,6 +54,7 @@                       , tasty-hunit >= 0.10 && < 0.11
                       , numeric-prelude >= 0.4.4 && < 0.5
                       , matrix >= 0.3.6.0 && < 0.4
+                      , unordered-containers >= 0.2.17.0 && < 0.3
                       , hspray
   Default-Language:     Haskell2010
   ghc-options:         -Wall
src/Math/Algebra/Hspray.hs view
@@ -85,31 +85,29 @@   , (*.)
   , constPoly
   , polyFromCoeffs
-  , outerVariable
+  , soleParameter
   , constQPoly
   , qpolyFromCoeffs
-  , outerQVariable
+  , qsoleParameter
   , evalRatioOfPolynomials
-  -- * Symbolic sprays 
-  , SymbolicSpray
-  , SymbolicQSpray
-  , prettySymbolicSprayX1X2X3
-  , prettySymbolicSprayXYZ
-  , prettySymbolicSpray
-  , prettySymbolicSpray'
-  , prettySymbolicQSprayX1X2X3
-  , prettySymbolicQSprayXYZ
-  , prettySymbolicQSpray
-  , prettySymbolicQSpray'
-  , simplifySymbolicSpray
-  , evalSymbolicSpray
-  , evalSymbolicSpray'
-  , evalSymbolicSpray''
+  -- * One-parameter sprays 
+  , OneParameterSpray
+  , OneParameterQSpray
+  , prettyOneParameterSprayX1X2X3
+  , prettyOneParameterSprayXYZ
+  , prettyOneParameterSpray
+  , prettyOneParameterSpray'
+  , prettyOneParameterQSprayX1X2X3
+  , prettyOneParameterQSprayXYZ
+  , prettyOneParameterQSpray
+  , prettyOneParameterQSpray'
+  , evalOneParameterSpray
+  , evalOneParameterSpray'
+  , evalOneParameterSpray''
   -- * Ratios of sprays
   , RatioOfSprays (..)
   , RatioOfQSprays
-  , ParametricSpray
-  , ParametricQSpray
+  , (%:%)
   , (%//%)
   , (%/%)
   , isConstantRatioOfSprays
@@ -122,7 +120,6 @@   , asRatioOfSprays
   , evalRatioOfSprays
   , substituteRatioOfSprays
-  , jacobiPolynomial
   , fromRatioOfPolynomials
   , fromRatioOfQPolynomials
   , showRatioOfSprays
@@ -140,6 +137,28 @@   , prettyRatioOfNumSpraysX1X2X3
   , prettyRatioOfNumSprays
   , prettyRatioOfNumSprays'
+  -- Parametric sprays
+  , SimpleParametricSpray
+  , SimpleParametricQSpray
+  , ParametricSpray
+  , ParametricQSpray
+  , canCoerceToSimpleParametricSpray
+  , asSimpleParametricSprayUnsafe
+  , asSimpleParametricSpray
+  , fromOneParameterSpray
+  , fromOneParameterQSpray
+  , parametricSprayToOneParameterSpray
+  , parametricQSprayToOneParameterQSpray
+  , gegenbauerPolynomial
+  , jacobiPolynomial
+  , numberOfParameters
+  , changeParameters
+  , substituteParameters
+  , evalParametricSpray
+  , prettyParametricQSprayABCXYZ
+  , prettyParametricQSpray
+  , prettySimpleParametricQSprayABCXYZ
+  , prettySimpleParametricQSpray
   -- * Queries on a spray
   , getCoefficient
   , getConstantTerm
@@ -182,9 +201,9 @@   , isPolynomialOf
   , bombieriSpray
   , collinearSprays
-  , gegenbauerPolynomial
   ) where
 import qualified Algebra.Additive              as AlgAdd
+import qualified Algebra.Differential          as AlgDiff
 import qualified Algebra.Field                 as AlgField
 import qualified Algebra.Module                as AlgMod
 import qualified Algebra.RightModule           as AlgRightMod
@@ -214,7 +233,9 @@                                                 )
 import qualified Data.Matrix                   as DM
 import           Data.Maybe                     ( isJust
-                                                , fromJust, fromMaybe
+                                                , isNothing
+                                                , fromJust
+                                                , fromMaybe
                                                 )
 import           Data.Ord                       ( comparing )
 import qualified Data.Ratio                    as DR
@@ -239,7 +260,7 @@ import           Data.Tuple.Extra               ( both )
 import qualified MathObj.Matrix                as MathMatrix
 import qualified MathObj.Polynomial            as MathPol
-import           Number.Ratio                   ( T ( (:%) ) )
+import           Number.Ratio                   ( T ( (:%) ), (%) )
 import qualified Number.Ratio                  as NumberRatio
 -- import qualified Algebra.PrincipalIdealDomain  as AlgPID
 -- import qualified Algebra.Units  as AlgUnits
@@ -250,33 +271,10 @@ 
 -- | A spray represents a multivariate polynomial so it has some variables. We 
 -- introduce a class because it will be assigned to the ratios of sprays too.
-class HasVariables a where
-
-  -- | The type of the objects the variables represent
-  type family VariablesType a
-
-  -- | Evaluation (replacing the variables by some values)
-  --
-  -- >>> x = lone 1 :: Spray Int
-  -- >>> y = lone 2 :: Spray Int
-  -- >>> spray = 2*^x^**^2 ^-^ 3*^y
-  -- >>> evaluate spray [2, 1]
-  -- 5
-  evaluate :: a -> [VariablesType a] -> VariablesType a
-
-  -- | Substitution (partial evaluation)
-  --
-  -- >>> x1 = lone 1 :: Spray Int
-  -- >>> x2 = lone 2 :: Spray Int
-  -- >>> x3 = lone 3 :: Spray Int
-  -- >>> spray = x1^**^2 ^-^ x2 ^+^ x3 ^-^ unitSpray
-  -- >>> spray' = substitute [Just 2, Nothing, Just 3] spray
-  -- >>> putStrLn $ prettyNumSprayX1X2X3 "x" spray'
-  -- -x2 + 6 
-  substitute :: [Maybe (VariablesType a)] -> a -> a
+class HasVariables b where
 
   -- | Number of variables
-  numberOfVariables :: a -> Int
+  numberOfVariables :: b -> Int
 
   -- | Permutes the variables
   --
@@ -290,16 +288,16 @@   -- prop> permuteVariables [3, 1, 2] spray == f x3 x1 x2
   permuteVariables :: 
        [Int] -- ^ permutation 
-    -> a     -- ^ the object whose variables will be permuted
-    -> a     -- ^ the object with permuted variables
+    -> b     -- ^ the object whose variables will be permuted
+    -> b     -- ^ the object with permuted variables
 
   -- | Swaps two variables 
   -- 
   -- prop> swapVariables (1, 3) x == permuteVariables [3, 2, 1] x
   swapVariables :: 
        (Int, Int) -- ^ the indices of the variables to be swapped (starting at 1) 
-    -> a          -- ^ the object whose variables will be swapped
-    -> a          -- ^ the object with swapped variables
+    -> b          -- ^ the object whose variables will be swapped
+    -> b          -- ^ the object with swapped variables
 
   -- | Derivative 
   --
@@ -311,26 +309,75 @@   -- 2
   derivative :: 
        Int -- ^ index of the variable of differentiation (starting at 1)
-    -> a   -- ^ the object to be derivated
-    -> a   -- ^ the derivated object
+    -> b   -- ^ the object to be derivated
+    -> b   -- ^ the derivated object
 
+  -- | The type of the coefficients (this is @a@ for both @Spray a@ and @RatioOfSprays a@)
+  type family BaseRing b
+
+  -- | The type of the variables (this is @Spray a@ for both @Spray a@ and @RatioOfSprays a@)
+  type family VariablesType b
+
+  -- | Evaluation (replacing the variables by some values)
+  --
+  -- >>> x = lone 1 :: Spray Int
+  -- >>> y = lone 2 :: Spray Int
+  -- >>> spray = 2*^x^**^2 ^-^ 3*^y
+  -- >>> evaluate spray [2, 1]
+  -- 5
+  evaluate :: b -> [BaseRing b] -> BaseRing b
+
+  -- | Flipped version of @evaluate@
+  --
+  -- >>> x = lone 1 :: Spray Int
+  -- >>> y = lone 2 :: Spray Int
+  -- >>> spray = 2*^x^**^2 ^-^ 3*^y
+  -- >>> evaluateAt [2, 1] spray
+  -- 5
+  evaluateAt :: [BaseRing b] -> b -> BaseRing b
+  evaluateAt = flip evaluate
+
+  -- | Substitution (partial evaluation)
+  --
+  -- >>> x1 = lone 1 :: Spray Int
+  -- >>> x2 = lone 2 :: Spray Int
+  -- >>> x3 = lone 3 :: Spray Int
+  -- >>> spray = x1^**^2 ^-^ x2 ^+^ x3 ^-^ unitSpray
+  -- >>> spray' = substitute [Just 2, Nothing, Just 3] spray
+  -- >>> putStrLn $ prettyNumSprayX1X2X3 "x" spray'
+  -- -x2 + 6 
+  substitute :: [Maybe (BaseRing b)] -> b -> b
+
+  -- | Change variables
+  --
+  -- >>> x = lone 1 :: Spray Int
+  -- >>> y = lone 2 :: Spray Int
+  -- >>> spray = x ^*^ y
+  -- >>> spray' = changeVariables spray [x ^+^ y, x ^-^ y]
+  -- >>> putStrLn $ prettyNumSpray' spray'
+  -- X^2 - Y^2
+  changeVariables :: 
+       b                 -- ^ object with variables such as a spray
+    -> [VariablesType b] -- ^ list of new variables
+    -> b
+
 -- | Whether an object of class `HasVariables` is constant
-isConstant :: HasVariables a => a -> Bool
+isConstant :: HasVariables b => b -> Bool
 isConstant f = numberOfVariables f == 0
 
 -- | Whether an object of class `HasVariables` is univariate; it is considered 
 -- that it is univariate if it is constant
-isUnivariate :: HasVariables a => a -> Bool
+isUnivariate :: HasVariables b => b -> Bool
 isUnivariate f = numberOfVariables f <= 1
 
 -- | Whether an object of class `HasVariables` is bivariate; it is considered 
 -- that it is bivariate if it is univariate
-isBivariate :: HasVariables a => a -> Bool
+isBivariate :: HasVariables b => b -> Bool
 isBivariate f = numberOfVariables f <= 2
 
 -- | Whether an object of class `HasVariables` is trivariate; it is considered 
 -- that it is trivariate if it is bivariate
-isTrivariate :: HasVariables a => a -> Bool
+isTrivariate :: HasVariables b => b -> Bool
 isTrivariate f = numberOfVariables f <= 3
 
 infixr 7 />
@@ -339,7 +386,7 @@ x /> lambda = AlgField.recip lambda AlgMod.*> x
 
 infixr 7 .^
--- | Scale by an integer (I do not find this operation in /numeric-prelude/)
+-- | Scale by an integer (I do not find this operation in __numeric-prelude__)
 --
 -- prop> 3 .^ x == x Algebra.Additive.+ x Algebra.Additive.+ x
 (.^) :: (AlgAdd.C a, Eq a) => Int -> a -> a
@@ -353,7 +400,7 @@       in go
 
 
--- Univariate polynomials -----------------------------------------------------
+-- Univariate polynomials and ratios of univariate polynomials ----------------
 
 newtype A a = A a 
   deriving
@@ -371,24 +418,91 @@ type QPolynomial          = Polynomial Rational'
 type RatioOfQPolynomials  = RatioOfPolynomials Rational'
 
-{- instance (Eq a, AlgField.C a) => HasVariables (Polynomial a) where
+instance (Eq a, AlgField.C a) => HasVariables (Polynomial a) where
+  --
   numberOfVariables :: Polynomial a -> Int
   numberOfVariables p = case MathPol.degree p of
     Nothing -> 0
     Just d  -> min 1 d
+  --
+  type BaseRing (Polynomial a) = a
+  --
+  type VariablesType (Polynomial a) = Polynomial a
+  --
+  evaluate :: Polynomial a -> [a] -> a
+  evaluate p xs = get (MathPol.evaluate p (A (xs !! 0)))
+    where
+      get (A x) = x
+  --
+  substitute :: [Maybe a] -> Polynomial a -> Polynomial a
+  substitute x p = 
+    if isNothing (x !! 0)
+      then p
+      else constPoly (evaluate p [fromJust $ x !! 0])
+  -- 
+  permuteVariables :: [Int] -> Polynomial a -> Polynomial a
+  permuteVariables = error "permuteVariables: there is only one variable."
+  -- 
+  swapVariables :: (Int, Int) -> Polynomial a -> Polynomial a
+  swapVariables = error "swapVariables: there is only one variable."
+  --
+  derivative :: Int -> Polynomial a -> Polynomial a
+  derivative i p = 
+    if i == 1 
+      then AlgDiff.differentiate p
+      else constPoly AlgAdd.zero
+  --
+  changeVariables :: Polynomial a -> [Polynomial a] -> Polynomial a
+  changeVariables p ps = MathPol.compose p (ps !! 0)
 
 instance (Eq a, AlgField.C a) => HasVariables (RatioOfPolynomials a) where
   numberOfVariables :: RatioOfPolynomials a -> Int
   numberOfVariables (p :% q) = 
     max (numberOfVariables p) (numberOfVariables q)
- -}
+  --
+  type BaseRing (RatioOfPolynomials a) = a
+  --
+  type VariablesType (RatioOfPolynomials a) = Polynomial a
+  --
+  evaluate :: RatioOfPolynomials a -> [a] -> a
+  evaluate r xs = evaluate (NumberRatio.numerator r) xs AlgField./ 
+    evaluate (NumberRatio.denominator r) xs
+  --
+  substitute :: [Maybe a] -> RatioOfPolynomials a -> RatioOfPolynomials a
+  substitute x r = 
+    if isNothing (x !! 0)
+      then r
+      else substitute x (NumberRatio.numerator r) %
+        substitute x (NumberRatio.denominator r)
+  -- 
+  permuteVariables :: [Int] -> RatioOfPolynomials a -> RatioOfPolynomials a
+  permuteVariables = error "permuteVariables: there is only one variable."
+  -- 
+  swapVariables :: (Int, Int) -> RatioOfPolynomials a -> RatioOfPolynomials a
+  swapVariables = error "swapVariables: there is only one variable."
+  --
+  derivative :: Int -> RatioOfPolynomials a -> RatioOfPolynomials a
+  derivative i r = 
+    if i == 1 
+      then 
+        (p' AlgRing.* q AlgAdd.- p AlgRing.* q') % q AlgRing.^ 2
+      else constPoly AlgAdd.zero :% constPoly AlgRing.one
+        where 
+          p = NumberRatio.numerator r
+          q = NumberRatio.denominator r
+          p' = AlgDiff.differentiate p
+          q' = AlgDiff.differentiate q
+  --
+  changeVariables :: RatioOfPolynomials a -> [Polynomial a] -> RatioOfPolynomials a
+  changeVariables r ps = changeVariables (NumberRatio.numerator r) ps %
+    changeVariables (NumberRatio.denominator r) ps 
 
--- | Division of univariate polynomials; this is an application of `:%` 
+{- -- | 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
@@ -401,8 +515,8 @@   (*>) :: Polynomial a -> RatioOfPolynomials a -> RatioOfPolynomials a
   p *> r = NumberRatio.scale p r 
 
-instance (Eq a, AlgField.C a) => AlgMod.C (Polynomial a) (SymbolicSpray a) where
-  (*>) :: Polynomial a -> SymbolicSpray a -> SymbolicSpray a
+instance (Eq a, AlgField.C a) => AlgMod.C (Polynomial a) (OneParameterSpray a) where
+  (*>) :: Polynomial a -> OneParameterSpray a -> OneParameterSpray a
   p *> r = constantSpray (p NumberRatio.:% AlgRing.one) ^*^ r
 
 infixr 7 *.
@@ -418,10 +532,10 @@ 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` 
-outerVariable :: AlgRing.C a => Polynomial a
-outerVariable = polyFromCoeffs [AlgAdd.zero, AlgRing.one] 
+-- | The variable of a univariate polynomial; it is called \"soleParameter\" because 
+-- this it represents the parameter of a `OneParameterSpray` spray
+soleParameter :: AlgRing.C a => Polynomial a
+soleParameter = polyFromCoeffs [AlgAdd.zero, AlgRing.one] 
 
 -- | Constant rational univariate polynomial
 -- 
@@ -439,13 +553,12 @@ qpolyFromCoeffs :: [Rational'] -> QPolynomial
 qpolyFromCoeffs = polyFromCoeffs
 
--- | 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) 
+-- | The variable of a univariate rational polynomial; it is called \"qsoleParameter\" 
+-- because it represents the parameter of a `OneParameterQSpray` spray 
 --
--- prop> outerQVariable == qpolyFromCoeffs [0, 1] 
-outerQVariable :: QPolynomial
-outerQVariable = qpolyFromCoeffs [0, 1] 
+-- prop> qsoleParameter == qpolyFromCoeffs [0, 1] 
+qsoleParameter :: QPolynomial
+qsoleParameter = qpolyFromCoeffs [0, 1] 
 
 {- 
 -- show a ratio, helper function
@@ -482,7 +595,7 @@ bracify :: (String, String) -> String -> String
 bracify (lbrace, rbrace) x = lbrace ++ x ++ rbrace 
 
--- | helper function for prettyRatioOfPolynomials (and prettySymbolicSpray)
+-- | helper function for prettyRatioOfPolynomials (and prettyOneParameterSpray)
 showRatioOfPolynomials :: forall a. (Eq a, AlgField.C a) 
                   => (Spray a -> String) -> RatioOfPolynomials a -> String
 showRatioOfPolynomials sprayShower polysRatio = 
@@ -506,7 +619,7 @@   -> String 
 prettyRatioOfQPolynomials var = showRatioOfPolynomials (prettyQSprayXYZ' [var])
 
--- | helper function for prettyRatioOfPolynomials (and prettySymbolicSpray)
+-- | helper function for prettyRatioOfPolynomials (and prettyOneParameterSpray)
 showQpol :: forall a. (Eq a, AlgField.C a) 
          => Polynomial a -> String -> (a -> String) -> Bool -> String
 showQpol pol variable showCoeff brackets = if brackets 
@@ -529,7 +642,7 @@           _ -> showCoeff' i (coeffs !! i) ++ variable ++ "^" ++ show i
     polyString = unpack (intercalate (pack " + ") terms)
 
--- | helper function for prettyRatioOfPolynomials (and prettySymbolicSpray)
+-- | helper function for prettyRatioOfPolynomials (and prettyOneParameterSpray)
 showQpolysRatio :: forall a. (Eq a, AlgField.C a) 
                    => String -> (a -> String) -> RatioOfPolynomials a -> String
 showQpolysRatio var showCoeff polysRatio = numeratorString ++ denominatorString
@@ -571,141 +684,142 @@       MathPol.evaluate (NumberRatio.denominator polysRatio) (A value)
 
 
--- Symbolic sprays ------------------------------------------------------------
+-- One-parameter sprays -------------------------------------------------------
 
-type SymbolicSpray a = Spray (RatioOfPolynomials a)
-type SymbolicQSpray  = SymbolicSpray Rational'
+type OneParameterSpray a = Spray (RatioOfPolynomials a)
+type OneParameterQSpray  = OneParameterSpray Rational'
 
--- | simplifies a ratio of polynomials (simply by multiplying it by one)
+{- -- | 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 simplifyRatioOfPolynomials
-
--- | Pretty form of a symbolic spray, using a string (typically a letter) 
+-- one-parameter spray
+simplifyOneParameterSpray :: 
+  (Eq a, AlgField.C a) => OneParameterSpray a -> OneParameterSpray a
+simplifyOneParameterSpray = HM.map simplifyRatioOfPolynomials
+ -}
+-- | Pretty form of a one-parameter spray, using a string (typically a letter) 
 -- followed by an index to denote the variables
-prettySymbolicSprayX1X2X3 ::
+prettyOneParameterSprayX1X2X3 ::
      (Eq a, Show a, AlgField.C 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              -- ^ string to denote the parameter of the spray, e.g. @"a"@
+  -> String              -- ^ typically a letter, to denote the non-indexed variables
+  -> OneParameterSpray a -- ^ a one-parameter spray; note that this function does not simplify it
   -> String 
-prettySymbolicSprayX1X2X3 a = showSprayX1X2X3 (prettyRatioOfPolynomials a) ("{ ", " }")
+prettyOneParameterSprayX1X2X3 a = showSprayX1X2X3 (prettyRatioOfPolynomials a) ("{ ", " }")
 
--- | Pretty form of a symbolic spray, using some given strings (typically some 
+-- | Pretty form of a one-parameter 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 ::
+-- @prettyOneParameterQSprayX1X2X3 a@ where @a@ is the first provided letter
+prettyOneParameterSprayXYZ ::
      (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              -- ^ string to denote the parameter of the spray, e.g. @"a"@
+  -> [String]            -- ^ typically some letters, to denote the main variables
+  -> OneParameterSpray a -- ^ a one-parameter spray; note that this function does not simplify it
   -> String 
-prettySymbolicSprayXYZ a = showSprayXYZ (prettyRatioOfPolynomials a) ("{ ", " }")
+prettyOneParameterSprayXYZ a = showSprayXYZ (prettyRatioOfPolynomials a) ("{ ", " }")
 
--- | Pretty form of a symbolic spray; see the definition below and see
--- `prettySymbolicSprayXYZ`
+-- | Pretty form of a one-parameter spray; see the definition below and see
+-- `prettyOneParameterSprayXYZ`
 --
--- prop> prettySymbolicSpray a spray == prettySymbolicSprayXYZ a ["x","y","z"] spray
-prettySymbolicSpray ::
+-- prop> prettyOneParameterSpray a spray == prettyOneParameterSprayXYZ a ["x","y","z"] spray
+prettyOneParameterSpray ::
      (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              -- ^ string to denote the parameter of the spray, e.g. @"a"@
+  -> OneParameterSpray a -- ^ a one-parameter spray; note that this function does not simplify it
   -> String 
-prettySymbolicSpray a = prettySymbolicSprayXYZ a ["x", "y", "z"]
+prettyOneParameterSpray a = prettyOneParameterSprayXYZ a ["x", "y", "z"]
 
--- | Pretty form of a symbolic spray; see the definition below and see
--- `prettySymbolicSprayXYZ`
+-- | Pretty form of a one-parameter spray; see the definition below and see
+-- `prettyOneParameterSprayXYZ`
 --
--- prop> prettySymbolicSpray' a spray == prettySymbolicSprayXYZ a ["X","Y","Z"] spray
-prettySymbolicSpray' ::
+-- prop> prettyOneParameterSpray' a spray == prettyOneParameterSprayXYZ a ["X","Y","Z"] spray
+prettyOneParameterSpray' ::
      (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              -- ^ string to denote the parameter of the spray, e.g. @"a"@
+  -> OneParameterSpray a -- ^ a one-parameter spray; note that this function does not simplify it
   -> String 
-prettySymbolicSpray' a = prettySymbolicSprayXYZ a ["X", "Y", "Z"]
+prettyOneParameterSpray' a = prettyOneParameterSprayXYZ a ["X", "Y", "Z"]
 
--- | Pretty form of a symbolic rational spray, using a string (typically a letter) 
+-- | Pretty form of a one-parameter rational spray, using a string (typically a letter) 
 -- followed by an index to denote the variables
-prettySymbolicQSprayX1X2X3 ::
-     String          -- ^ usually a letter, to denote the outer variable of the spray, e.g. @"a"@
+prettyOneParameterQSprayX1X2X3 ::
+     String          -- ^ usually a letter, to denote the parameter of the spray, e.g. @"a"@
   -> String          -- ^ usually a letter, to denote the non-indexed variables of the spray
-  -> SymbolicQSpray  -- ^ a symbolic rational spray; note that this function does not simplify it
+  -> OneParameterQSpray  -- ^ a one-parameter rational spray; note that this function does not simplify it
   -> String 
-prettySymbolicQSprayX1X2X3 a x = 
+prettyOneParameterQSprayX1X2X3 a x = 
   showSpray (prettyRatioOfQPolynomials a) ("{ ", " }") (showMonomialsX1X2X3 x)
 
--- | Pretty form of a symbolic rational spray, using some given strings (typically some 
+-- | Pretty form of a one-parameter 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          -- ^ usually a letter, 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 rational spray; note that this function does not simplify it
+-- @prettyOneParameterQSprayX1X2X3 a@ where @a@ is the first provided letter
+prettyOneParameterQSprayXYZ ::
+     String             -- ^ usually a letter, to denote the parameter of the spray, e.g. @"a"@
+  -> [String]           -- ^ usually some letters, to denote the variables of the spray
+  -> OneParameterQSpray -- ^ a one-parameter rational spray; note that this function does not simplify it
   -> String 
-prettySymbolicQSprayXYZ a letters = 
+prettyOneParameterQSprayXYZ a letters = 
   showSpray (prettyRatioOfQPolynomials a) ("{ ", " }") (showMonomialsXYZ letters)
 
--- | Pretty form of a symbolic rational spray, using @"x"@, @"y"@ and @"z"@ for the variables 
+-- | Pretty form of a one-parameter 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          -- ^ usually a letter, to denote the outer variable of the spray, e.g. @"a"@
-  -> SymbolicQSpray  -- ^ the symbolic rational spray to be printed; note that this function does not simplify it
+-- prop> prettyOneParameterQSpray a == prettyOneParameterQSprayXYZ a ["x","y","z"]
+prettyOneParameterQSpray ::
+     String             -- ^ usually a letter, to denote the parameter of the spray, e.g. @"a"@
+  -> OneParameterQSpray -- ^ the one-parameter rational spray to be printed; note that this function does not simplify it
   -> String 
-prettySymbolicQSpray a = prettySymbolicQSprayXYZ a ["x", "y", "z"] 
+prettyOneParameterQSpray a = prettyOneParameterQSprayXYZ a ["x", "y", "z"] 
 
--- | Pretty form of a symbolic rational spray, using @"X"@, @"Y"@ and @"Z"@ for the variables 
+-- | Pretty form of a one-parameter 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          -- ^ usually a letter, to denote the outer variable of the spray, e.g. @"a"@
-  -> SymbolicQSpray  -- ^ the symbolic rational spray to be printed; note that this function does not simplify it
+-- prop> prettyOneParameterQSpray' a = prettyOneParameterQSprayXYZ a ["X","Y","Z"]
+prettyOneParameterQSpray' ::
+     String              -- ^ usually a letter, to denote the parameter of the spray, e.g. @"a"@
+  -> OneParameterQSpray  -- ^ the one-parameter rational spray to be printed; note that this function does not simplify it
   -> String 
-prettySymbolicQSpray' a = prettySymbolicQSprayXYZ a ["X", "Y", "Z"] 
+prettyOneParameterQSpray' a = prettyOneParameterQSprayXYZ a ["X", "Y", "Z"] 
 
--- | Substitutes a value to the outer variable of a symbolic spray 
+-- | Substitutes a value to the parameter of a one-parameter spray 
 -- (the variable occuring in the coefficients)
-evalSymbolicSpray :: (Eq a, AlgField.C a) => SymbolicSpray a -> a -> Spray a
-evalSymbolicSpray spray x = 
+evalOneParameterSpray :: 
+  (Eq a, AlgField.C a) => OneParameterSpray a -> a -> Spray a
+evalOneParameterSpray spray x = 
   removeZeroTerms $ HM.map (evalRatioOfPolynomials x) spray 
 
--- | Substitutes a value to the outer variable of a symbolic spray as well 
--- as some values to the main variables of this spray
-evalSymbolicSpray' :: (Eq a, AlgField.C a) 
-  => SymbolicSpray a -- ^ symbolic spray to be evaluated
-  -> a               -- ^ a value for the outer variable
-  -> [a]             -- ^ some values for the inner variables 
+-- | Substitutes a value to the parameter of a one-parameter spray as well 
+-- as some values to the variables of this spray
+evalOneParameterSpray' :: (Eq a, AlgField.C a) 
+  => OneParameterSpray a -- ^ one-parameter spray to be evaluated
+  -> a                   -- ^ a value for the parameter
+  -> [a]                 -- ^ some values for the variables 
   -> a
-evalSymbolicSpray' spray x xs = if length xs >= numberOfVariables spray 
-  then evalSpray (evalSymbolicSpray spray x) xs
-  else error "evalSymbolicSpray': not enough values provided."
+evalOneParameterSpray' spray x xs = if length xs >= numberOfVariables spray 
+  then evalSpray (evalOneParameterSpray spray x) xs
+  else error "evalOneParameterSpray': not enough values provided."
 
--- | helper function for evalSymbolicSpray''
-evalSymbolicMonomial :: (Eq a, AlgField.C a) 
+-- | helper function for evalOneParameterSpray''
+evalOneParameterMonomial :: (Eq a, AlgField.C a) 
   => [a] -> Monomial (RatioOfPolynomials a) -> RatioOfPolynomials a
-evalSymbolicMonomial xs (powers, coeff) = 
+evalOneParameterMonomial xs (powers, coeff) = 
   AlgRing.product (zipWith (AlgRing.^) xs pows) *. coeff
   where 
     pows = DF.toList (fromIntegral <$> exponents powers)
 
--- | Substitutes some values to the main variables of a symbolic spray
-evalSymbolicSpray'' ::
-  (Eq a, AlgField.C a) => SymbolicSpray a -> [a] -> RatioOfPolynomials a
-evalSymbolicSpray'' spray xs = if length xs >= numberOfVariables spray
-  then AlgAdd.sum $ map (evalSymbolicMonomial xs) (HM.toList spray)
-  else error "evalSymbolicSpray'': not enough values provided."
+-- | Substitutes some values to the variables of a one-parameter spray
+evalOneParameterSpray'' ::
+  (Eq a, AlgField.C a) => OneParameterSpray a -> [a] -> RatioOfPolynomials a
+evalOneParameterSpray'' spray xs = if length xs >= numberOfVariables spray
+  then AlgAdd.sum $ map (evalOneParameterMonomial xs) (HM.toList spray)
+  else error "evalOneParameterSpray'': not enough values provided."
 
 
 -- Sprays ---------------------------------------------------------------------
@@ -757,8 +871,10 @@ type QSpray' = Spray Rational'
 
 instance (AlgRing.C a, Eq a) => HasVariables (Spray a) where
-  type VariablesType (Spray a) = a
+  type BaseRing (Spray a) = a
   --
+  type VariablesType (Spray a) = Spray a
+  --
   evaluate :: Spray a -> [a] -> a
   evaluate spray xyz = if length xyz >= numberOfVariables spray 
     then evalSprayHelper xyz spray
@@ -786,6 +902,9 @@           pows''   = S.mapWithIndex f pows
           powers'' = simplifyPowers $ Powers pows'' nv
   --
+  changeVariables :: Spray a -> [Spray a] -> Spray a
+  changeVariables = composeSpray
+  --
   numberOfVariables :: Spray a -> Int
   numberOfVariables spray =
     if null powers then 0 else maximum (map nvariables powers)
@@ -1027,7 +1146,7 @@   where
     powers  = Powers S.empty 0
 
--- | Whether a spray is constant; this is an alias of `isConstant`
+-- | Whether a spray is constant; same as `isConstant`
 isConstantSpray :: (Eq a, AlgRing.C a) => Spray a -> Bool
 isConstantSpray = isConstant
 
@@ -1042,7 +1161,7 @@       where 
         pows = DF.toList (fromIntegral <$> exponents powers)
 
--- | Evaluates a spray; this is an alias of `evaluate`
+-- | Evaluates a spray; same as `evaluate`
 --
 -- >>> x = lone 1 :: Spray Int
 -- >>> y = lone 2 :: Spray Int
@@ -1053,7 +1172,7 @@ evalSpray = evaluate
 
 -- | Evaluates the coefficients of a spray with spray coefficients; 
--- see README for an example
+-- same as `substituteParameters`
 evalSpraySpray :: (Eq a, AlgRing.C a) => Spray (Spray a) -> [a] -> Spray a
 evalSpraySpray spray xyz = if length xyz >= n 
   then HM.map (evalSprayHelper xyz) spray
@@ -1061,32 +1180,11 @@     where 
       n = maximum (HM.elems $ HM.map numberOfVariables spray)
 
--- | Gegenbauer polynomials; we mainly provide them to give an example 
--- of the @Spray (Spray a)@ type
---
--- >>> gp = gegenbauerPolynomial 3
--- >>> putStrLn $ showSprayXYZ' (prettyQSprayXYZ ["alpha"]) ["X"] gp
--- ((4/3)*alpha^3 + 4*alpha^2 + (8/3)*alpha)*X^3 + (-2*alpha^2 - 2*alpha)*X
--- >>> putStrLn $ prettyQSpray'' $ evalSpraySpray gp [1]
--- 8*X^3 - 4*X
-gegenbauerPolynomial :: Int -> Spray (Spray Rational) 
-gegenbauerPolynomial n 
-  | n == 0 = unitSpray
-  | n == 1 = (2.^a) *^ x
-  | otherwise = 
-    (2.^(n'' ^+^ a) /^ n') *^ (x ^*^ gegenbauerPolynomial (n - 1))
-    ^-^ ((n'' ^+^ 2.^a ^-^ unitSpray) /^ n') *^ gegenbauerPolynomial (n - 2)
-  where 
-    x = lone 1 :: Spray (Spray Rational)
-    a = lone 1 :: Spray Rational
-    n'  = toRational n
-    n'' = constantSpray (n' - 1)
-
 -- | spray from monomial
 fromMonomial :: Monomial a -> Spray a
 fromMonomial (pows, coeff) = HM.singleton pows coeff
 
--- | Substitutes some variables in a spray by some values; this is an alias of `substitute`
+-- | Substitutes some variables in a spray by some values; same as `substitute`
 --
 -- >>> x1 = lone 1 :: Spray Int
 -- >>> x2 = lone 2 :: Spray Int
@@ -1103,8 +1201,7 @@ fromRationalSpray :: Spray Rational -> Spray Double
 fromRationalSpray = HM.map fromRational
 
--- | Sustitutes the variables of a spray with some sprays 
--- (e.g. change of variables)
+-- | Sustitutes the variables of a spray with some sprays; same as `changeVariables`
 --
 -- >>> x = lone 1 :: Spray Int
 -- >>> y = lone 2 :: Spray Int
@@ -2290,7 +2387,7 @@ -- Matrices -------------------------------------------------------------------
 
 -- | Determinant of a matrix with entries in a ring by using Laplace 
--- expansion (this is slow); the /numeric-prelude/ package provides some 
+-- expansion (this is slow); the __numeric-prelude__ package provides some 
 -- stuff to deal with matrices over a ring but it does not provide the 
 -- determinant
 detLaplace :: forall a. (Eq a, AlgRing.C a) => Matrix a -> a
@@ -2312,7 +2409,7 @@     times x y = if x == AlgAdd.zero then AlgAdd.zero else x AlgRing.* y
 
 -- | Determinant of a matrix over a ring by using Laplace expansion; this is 
--- the same as `detLaplace` but for a matrix from the /numeric-prelude/ 
+-- the same as `detLaplace` but for a matrix from the __numeric-prelude__ 
 -- package
 detLaplace' :: forall a. (Eq a, AlgRing.C a) => MathMatrix.T a -> a
 detLaplace' m = detLaplace (DM.fromLists $ MathMatrix.rows m) 
@@ -2342,7 +2439,7 @@ -- Ratios of sprays -----------------------------------------------------------
 
 -- | A @RatioOfSprays a@ object represents a fraction of two multivariate 
--- polynomials whose coefficients are of type @a@ which represents a field. 
+-- polynomials whose coefficients are of type @a@, which represents a field. 
 -- These two polynomials are represented by two @Spray a@ objects. Generally 
 -- we do not use this constructor to build a ratio of sprays: we use the `%//%`
 -- operator instead, because it always returns an irreducible ratio of sprays, 
@@ -2364,12 +2461,12 @@   deriving Show
 
 type RatioOfQSprays = RatioOfSprays Rational
-type ParametricSpray a = Spray (RatioOfSprays a)
-type ParametricQSpray = ParametricSpray Rational
 
 instance (Eq a, AlgField.C a) => HasVariables (RatioOfSprays a) where
-  type VariablesType (RatioOfSprays a) = a
+  type BaseRing (RatioOfSprays a) = a
   --
+  type VariablesType (RatioOfSprays a) = Spray a
+  --
   substitute :: [Maybe a] -> RatioOfSprays a -> RatioOfSprays a
   substitute subs (RatioOfSprays p q) = 
     substitute subs p %//% substitute subs q  
@@ -2377,6 +2474,15 @@   evaluate :: RatioOfSprays a -> [a] -> a
   evaluate (RatioOfSprays p q) xyz = evaluate p xyz AlgField./ evaluate q xyz
   --
+  changeVariables :: RatioOfSprays a -> [Spray a] -> RatioOfSprays a
+  changeVariables rOS newVariables = 
+    if length newVariables < numberOfVariables rOS
+      then 
+        error "changeVariables: not enough new variables provided."
+      else
+        changeVariables (_numerator rOS) newVariables 
+          %//% changeVariables (_denominator rOS) newVariables 
+  --
   numberOfVariables :: RatioOfSprays a -> Int
   numberOfVariables (RatioOfSprays p q) = 
     max (numberOfVariables p) (numberOfVariables q)
@@ -2462,30 +2568,43 @@   recip :: RatioOfSprays a -> RatioOfSprays a
   recip (RatioOfSprays p q) = RatioOfSprays q p
 
+infixl 7 %:%
+-- | Ratio of sprays from numerator and denominator, 
+-- __without reducing the fraction__
+(%:%) :: Spray a -> Spray a -> RatioOfSprays a 
+(%:%) = RatioOfSprays
+
 infixl 7 %//%
--- | Irreducible ratio of sprays from numerator and denominator
+-- | Irreducible ratio of sprays from numerator and denominator; alias of @(^/^)@
 (%//%) :: (Eq a, AlgField.C a) => Spray a -> Spray a -> RatioOfSprays a 
 (%//%) = irreducibleFraction 
 
-infixr 7 %/%
--- | Division of a ratio of sprays by a spray
+infixl 7 ^/^
+-- | Irreducible ratio of sprays from numerator and denominator; alias of @(%//%)@
+(^/^) :: (Eq a, AlgField.C a) => Spray a -> Spray a -> RatioOfSprays a 
+(^/^) = irreducibleFraction 
+
+infixl 7 %/%
+-- | Division of a ratio of sprays by a spray; the result is an 
+-- irreducible fraction
 (%/%) :: (Eq a, AlgField.C a) => RatioOfSprays a -> Spray a -> RatioOfSprays a 
 (%/%) rOS spray = rOS AlgRing.* RatioOfSprays unitSpray spray 
 
-instance (Eq a, AlgField.C a) => AlgMod.C a (ParametricSpray a) where
-  (*>) :: a -> ParametricSpray a -> ParametricSpray a
-  lambda *> pspray = HM.map (lambda AlgMod.*>) pspray
-
-instance (Eq a, AlgField.C a) => AlgRightMod.C a (ParametricSpray a) where
-  (<*) :: ParametricSpray a -> a -> ParametricSpray a
-  pspray <* lambda = HM.map (AlgRightMod.<* lambda) pspray
-
--- | Whether a ratio of sprays is constant; this is an alias of `isConstant`
+-- | Whether a ratio of sprays is constant; same as `isConstant`
 isConstantRatioOfSprays :: (Eq a, AlgField.C a) => RatioOfSprays a -> Bool
 isConstantRatioOfSprays = isConstant
 
--- | Wheter a ratio of sprays actually is polynomial, that is, whether its 
+-- | Whether a ratio of sprays actually is polynomial, that is, whether its 
 -- denominator is a constant spray (and then it should be the unit spray)
+--
+-- >>> x = qlone 1
+-- >>> y = qlone 2
+-- >>> p = x^**^4 ^-^ y^**^4
+-- >>> q = x ^-^ y
+-- >>> isPolynomialRatioOfSprays $ p %//% q
+-- True
+-- >>> isPolynomialRatioOfSprays $ p %:% q
+-- False
 isPolynomialRatioOfSprays :: (Eq a, AlgRing.C a) => RatioOfSprays a -> Bool
 isPolynomialRatioOfSprays = isConstant . _denominator
 
@@ -2503,11 +2622,11 @@ constantRatioOfSprays :: (Eq a, AlgRing.C a) => a -> RatioOfSprays a
 constantRatioOfSprays x = asRatioOfSprays (constantSpray x)
 
--- | Evaluates a ratio of sprays; this is an alias of `evaluate`
+-- | Evaluates a ratio of sprays; same as `evaluate`
 evalRatioOfSprays :: (Eq a, AlgField.C a) => RatioOfSprays a -> [a] -> a
 evalRatioOfSprays = evaluate
 
--- | Substitutes some variables in a ratio of sprays; this is an alias of `substitute`
+-- | Substitutes some variables in a ratio of sprays; same as `substitute`
 substituteRatioOfSprays :: 
   (Eq a, AlgField.C a) => [Maybe a] -> RatioOfSprays a -> RatioOfSprays a
 substituteRatioOfSprays = substitute
@@ -2534,35 +2653,6 @@     (qPolynomialToQSpray $ NumberRatio.numerator rop) 
     (qPolynomialToQSpray $ NumberRatio.denominator rop)  
 
--- | Jacobi polynomial
-jacobiPolynomial :: Int -> Spray RatioOfQSprays
-jacobiPolynomial n 
-  | n == 0 = unitSpray
-  | n == 1 = 
-      asSpray (alpha0 ^+^ cst 1) ^+^  
-        (asRatioOfSprays ((alpha0 ^+^ beta0 ^+^ cst 2) /^ 2) *^ 
-          (x ^-^ unitSpray))
-  | otherwise = 
-      lambda1 ^*^ jacobiPolynomial (n-1) ^-^ lambda2 ^*^ jacobiPolynomial (n-2)
-  where
-    cst :: Rational -> QSpray
-    cst = constantSpray
-    alpha0 = qlone 1
-    beta0  = qlone 2
-    x = lone 1 :: Spray RatioOfQSprays
-    n0 = cst (toRational n)
-    a0 = n0 ^+^ alpha0
-    b0 = n0 ^+^ beta0
-    c0 = a0 ^+^ b0
-    asSpray :: QSpray -> Spray RatioOfQSprays
-    asSpray = constantSpray . asRatioOfSprays
-    lambda0 = asRatioOfSprays $ 2.^(n0^*^(c0 ^-^ n0)^*^(c0 ^-^ cst 2))
-    lambda1 = (asRatioOfSprays (c0 ^-^ cst 1) AlgMod.*> 
-      ((asRatioOfSprays (c0^*^(c0 ^-^ cst 2)) *^ x ) ^+^ 
-        asSpray ((a0 ^-^ b0)^*^(c0 ^-^ 2.^n0)))) /> lambda0
-    lambda2 = 
-      asSpray (2.^((a0 ^-^ cst 1)^*^(b0 ^-^ cst 1)^*^c0)) /> lambda0
-
 -- | General function to print a `RatioOfSprays` object
 showRatioOfSprays :: (Eq a, AlgRing.C a) 
   => ((Spray a, Spray a) -> (String, String)) -- ^ function which prints a pair of sprays that will be applied to the numerator and the denominator
@@ -2776,7 +2866,7 @@ -- | Prints a ratio of sprays with rational coefficients, printing the monomials 
 -- in the style of @"x1^2.x2.x3^3"@
 prettyRatioOfQSpraysX1X2X3 :: 
-     String         -- ^ typically a letter, to represent the variables
+     String         -- ^ typically a letter, to represent the non-indexed variables
   -> RatioOfQSprays
   -> String
 prettyRatioOfQSpraysX1X2X3 letter = 
@@ -2812,3 +2902,291 @@   -> String
 prettyRatioOfNumSpraysX1X2X3 letter = 
   showRatioOfNumSpraysX1X2X3 show letter ("[ ", " ]") " %//% "
+
+
+-- Parametric sprays ----------------------------------------------------------
+
+type SimpleParametricSpray a = Spray (Spray a)
+type SimpleParametricQSpray  = SimpleParametricSpray Rational
+type ParametricSpray a = Spray (RatioOfSprays a)
+type ParametricQSpray  = ParametricSpray Rational
+
+instance (Eq a, AlgRing.C a) => AlgMod.C a (SimpleParametricSpray a) where
+  (*>) :: a -> SimpleParametricSpray a -> SimpleParametricSpray a
+  lambda *> pspray = HM.map (lambda AlgMod.*>) pspray
+
+instance (Eq a, AlgRing.C a) => AlgRightMod.C a (SimpleParametricSpray a) where
+  (<*) :: SimpleParametricSpray a -> a -> SimpleParametricSpray a
+  pspray <* lambda = HM.map (AlgRightMod.<* lambda) pspray
+
+instance (Eq a, AlgField.C a) => AlgMod.C a (ParametricSpray a) where
+  (*>) :: a -> ParametricSpray a -> ParametricSpray a
+  lambda *> pspray = HM.map (lambda AlgMod.*>) pspray
+
+instance (Eq a, AlgField.C a) => AlgRightMod.C a (ParametricSpray a) where
+  (<*) :: ParametricSpray a -> a -> ParametricSpray a
+  pspray <* lambda = HM.map (AlgRightMod.<* lambda) pspray
+
+instance (Eq a, AlgField.C a) => AlgMod.C (Spray a) (ParametricSpray a) where
+  (*>) :: Spray a -> ParametricSpray a -> ParametricSpray a
+  spray *> pspray = asRatioOfSprays spray *^ pspray
+
+instance (Eq a, AlgField.C a) => AlgRightMod.C (Spray a) (ParametricSpray a) where
+  (<*) :: ParametricSpray a -> Spray a -> ParametricSpray a
+  pspray <* spray = asRatioOfSprays spray *^ pspray
+
+-- | Number of parameters in a parametric spray
+--
+-- >>> numberOfParameters (jacobiPolynomial 4)
+-- 2
+numberOfParameters :: HasVariables b => Spray b -> Int
+numberOfParameters pspray = 
+  if isZeroSpray pspray
+    then 0
+    else 
+      maximum (map numberOfVariables (HM.elems pspray))
+
+-- | Apply polynomial transformations to the parameters of a parametric spray; 
+-- e.g. you have a two-parameters polynomial \(P_{a, b}(X, Y, Z)\) and you want
+-- to get \(P_{a^2, b^2}(X, Y, Z)\), or the one-parameter polynomial 
+-- \(P_{a, a}(X, Y, Z)\)
+-- 
+-- >>> jp = jacobiPolynomial 4
+-- >>> a = qlone 1
+-- >>> b = qlone 2
+-- >>> changeParameters jp [a^**^2, b^**^2]
+changeParameters :: HasVariables b => Spray b -> [VariablesType b] -> Spray b
+changeParameters pspray newParameters = 
+  if length newParameters < numberOfParameters pspray
+    then 
+      error "changeParameters: not enough new parameters provided."
+    else 
+      HM.map (`changeVariables` newParameters) pspray
+
+-- | Substitutes some values to the parameters of a parametric spray
+--
+-- >>> jacobi3 = jacobiPolynomial 3
+-- >>> legendre3 = substituteParameters jp [0, 0]
+substituteParameters :: 
+    (HasVariables b, Eq (BaseRing b), AlgAdd.C (BaseRing b)) 
+  => Spray b 
+  -> [BaseRing b] 
+  -> Spray (BaseRing b) 
+substituteParameters pspray values = 
+  if length values < numberOfParameters pspray
+    then 
+      error "substituteParameters: not enough values provided."
+    else 
+      removeZeroTerms $ HM.map (evaluateAt values) pspray 
+
+-- | helper function for evalParametricSpray
+evalMonomial' :: 
+  (AlgMod.C (BaseRing b) b) => [BaseRing b] -> Monomial b -> b
+evalMonomial' xs (powers, coeff) = 
+  AlgRing.product (zipWith (AlgRing.^) xs pows) AlgMod.*> coeff
+  where 
+    pows = DF.toList (fromIntegral <$> exponents powers)
+
+-- | Substitutes some values to the variables of a parametric spray
+evalParametricSpray ::
+  (Eq b, AlgMod.C (BaseRing b) b, AlgRing.C b) 
+  => Spray b -> [BaseRing b] -> b
+evalParametricSpray spray xs = if length xs >= numberOfVariables spray
+  then AlgAdd.sum $ map (evalMonomial' xs) (HM.toList spray)
+  else error "evalParametricSpray: not enough values provided."
+
+-- | Whether the coefficients of a parametric spray polynomially 
+-- depend on their parameters; I do not know why, but it seems to be the case 
+-- for the Jacobi polynomials 
+--
+-- >>> canCoerceToSimpleParametricSpray (jacobiPolynomial 8)
+-- True
+canCoerceToSimpleParametricSpray :: 
+  (Eq a, AlgRing.C a) => ParametricSpray a -> Bool
+canCoerceToSimpleParametricSpray spray = 
+  all isPolynomialRatioOfSprays (HM.elems spray)
+
+-- | Coerces a parametric spray to a simple parametric spray, without 
+-- checking this makes sense with `canCoerceToSimpleParametricSpray`
+asSimpleParametricSprayUnsafe :: ParametricSpray a -> SimpleParametricSpray a
+asSimpleParametricSprayUnsafe = HM.map _numerator
+
+-- | Coerces a parametric spray to a simple parametric spray, after
+-- checking this makes sense with `canCoerceToSimpleParametricSpray`
+asSimpleParametricSpray :: 
+  (Eq a, AlgRing.C a) => ParametricSpray a -> SimpleParametricSpray a
+asSimpleParametricSpray spray = 
+  if canCoerceToSimpleParametricSpray spray 
+    then asSimpleParametricSprayUnsafe spray
+    else error $
+      "asSimpleParametricSpray: this parametric spray is not coercable" ++ 
+      " to a simple parametric spray."
+
+-- | Converts a `OneParameterSpray a` spray to a `ParametricSpray a`
+fromOneParameterSpray :: 
+  (Eq a, AlgRing.C a) => OneParameterSpray a -> ParametricSpray a
+fromOneParameterSpray = HM.map fromRatioOfPolynomials
+
+-- | Converts a `OneParameterQSpray` spray to a `ParametricQSpray`
+fromOneParameterQSpray :: OneParameterQSpray -> ParametricQSpray
+fromOneParameterQSpray = HM.map fromRatioOfQPolynomials
+
+-- | Converts a parametric spray to a one-parameter spray, without checking
+-- the conversion makes sense
+parametricSprayToOneParameterSpray :: 
+  forall a. (AlgRing.C a) => ParametricSpray a -> OneParameterSpray a
+parametricSprayToOneParameterSpray = HM.map toRatioOfPolynomials
+  where
+    toRatioOfPolynomials :: RatioOfSprays a -> RatioOfPolynomials a
+    toRatioOfPolynomials (RatioOfSprays p q) = 
+      toPolynomial p :% toPolynomial q
+      where
+        toPolynomial :: Spray a -> Polynomial a
+        toPolynomial spray = polyFromCoeffs coeffs
+          where
+            coeffs = map (\i -> getCoefficient [i] spray) [0 .. deg]
+            deg = maximum (0 : map (`index` 0) expnts)
+            powers = HM.keys spray
+            expnts  = filter (not . S.null) (map exponents powers)
+
+-- | Converts a rational parametric spray to a rational one-parameter spray, 
+-- without checking the conversion makes sense
+parametricQSprayToOneParameterQSpray :: ParametricQSpray -> OneParameterQSpray
+parametricQSprayToOneParameterQSpray = HM.map toRatioOfQPolynomials
+  where
+    toRatioOfQPolynomials :: RatioOfQSprays -> RatioOfQPolynomials
+    toRatioOfQPolynomials (RatioOfSprays p q) = 
+      toQPolynomial p :% toQPolynomial q
+      where
+        toQPolynomial :: QSpray -> QPolynomial
+        toQPolynomial spray = polyFromCoeffs coeffs'
+          where
+            coeffs' = map (\i -> f (getCoefficient [i] spray)) [0 .. deg]
+            f :: Rational -> Rational'
+            f r = DR.numerator r :% DR.denominator r
+            deg = maximum (0 : map (`index` 0) expnts)
+            powers = HM.keys spray
+            expnts  = filter (not . S.null) (map exponents powers)
+
+-- | [Gegenbauer polynomials](https://en.wikipedia.org/wiki/Gegenbauer_polynomials); 
+-- we mainly provide them to give an example of the @SimpleParametricSpray@ type
+--
+-- >>> gp = gegenbauerPolynomial 3
+-- >>> putStrLn $ prettySimpleParametricQSpray gp
+-- { (4/3)*a^3 + 4*a^2 + (8/3)*a }*X^3 + { -2*a^2 - 2*a }*X
+-- >>> putStrLn $ prettyQSpray'' $ substituteParameters gp [1]
+-- 8*X^3 - 4*X
+gegenbauerPolynomial :: Int -> SimpleParametricQSpray 
+gegenbauerPolynomial n 
+  | n == 0 = unitSpray
+  | n == 1 = (2.^a) *^ x
+  | otherwise = 
+    (2.^(n'' ^+^ a) /^ n') *^ (x ^*^ gegenbauerPolynomial (n - 1))
+    ^-^ ((n'' ^+^ 2.^a ^-^ unitSpray) /^ n') *^ gegenbauerPolynomial (n - 2)
+  where 
+    x = lone 1 :: SimpleParametricQSpray
+    a = lone 1 :: QSpray
+    n'  = toRational n
+    n'' = constantSpray (n' - 1)
+
+-- | [Jacobi polynomial](https://en.wikipedia.org/wiki/Jacobi_polynomials); 
+-- the @n@-th Jacobi polynomial is a univariate polynomial of degree @n@ with 
+-- two parameters, except for the case @n=0@ where it has no parameter
+jacobiPolynomial :: Int -> ParametricQSpray
+jacobiPolynomial n 
+  | n < 0  = error "jacobiPolynomial: `n` must be positive." 
+  | n == 0 = unitSpray
+  | n == 1 = 
+      asParametricQSpray (alpha0 ^+^ cst 1) ^+^  
+        (asRatioOfSprays ((alpha0 ^+^ beta0 ^+^ cst 2) /^ 2) *^ 
+          (x ^-^ unitSpray))
+  | otherwise = 
+      lambda1 ^*^ jacobiPolynomial (n-1) ^-^ lambda2 ^*^ jacobiPolynomial (n-2)
+  where
+    cst :: Rational -> QSpray
+    cst = constantSpray
+    alpha0 = qlone 1
+    beta0  = qlone 2
+    x = lone 1 :: ParametricQSpray
+    n0 = cst (toRational n)
+    a0 = n0 ^+^ alpha0
+    b0 = n0 ^+^ beta0
+    c0 = a0 ^+^ b0
+    asParametricQSpray :: QSpray -> ParametricQSpray
+    asParametricQSpray = constantSpray . asRatioOfSprays
+    lambda0 = asRatioOfSprays $ 2.^(n0^*^(c0 ^-^ n0)^*^(c0 ^-^ cst 2))
+    lambda1 = (asRatioOfSprays (c0 ^-^ cst 1) AlgMod.*> 
+      ((asRatioOfSprays (c0^*^(c0 ^-^ cst 2)) *^ x ) ^+^ 
+        asParametricQSpray ((a0 ^-^ b0)^*^(c0 ^-^ 2.^n0)))) /> lambda0
+    lambda2 = 
+      asParametricQSpray (2.^((a0 ^-^ cst 1)^*^(b0 ^-^ cst 1)^*^c0)) /> lambda0
+
+-- | Pretty form of a parametric rational spray, using some given strings (typically some 
+-- letters) to denote the parameters and some given strings (typically some letters) to 
+-- denote the variables
+--
+-- >>> type PQS = ParametricQSpray
+-- >>> :{
+-- >>> f :: (QSpray, QSpray) -> (PQS, PQS, PQS) -> PQS
+-- >>> f (a, b) (x, y, z) = 
+-- >>>   (a %:% (a ^+^ b)) *^ x^**^2  ^+^  (b %:% (a ^+^ b)) *^ (y ^*^ z)
+-- >>> :}
+-- >>> a = qlone 1
+-- >>> b = qlone 2
+-- >>> x = lone 1 :: PQS
+-- >>> y = lone 2 :: PQS
+-- >>> z = lone 3 :: PQS
+-- >>> pqs = f (a, b) (x, y, z)
+-- >>> putStrLn $ prettyParametricQSprayABCXYZ ["a","b"] ["X","Y","Z"] pqs
+-- { [ a ] %//% [ a + b ] }*X^2 + { [ b ] %//% [ a + b ] }*Y.Z
+prettyParametricQSprayABCXYZ ::
+     [String]           -- ^ usually some letters, to denote the parameters of the spray
+  -> [String]           -- ^ usually some letters, to denote the variables of the spray
+  -> ParametricQSpray   -- ^ a parametric rational spray
+  -> String 
+prettyParametricQSprayABCXYZ abc xyz spray = 
+  showSpray rOSShower ("{ ", " }") (showMonomialsXYZ xyz) spray
+  where
+    rOSShower = if numberOfParameters spray <= length abc
+      then prettyRatioOfQSpraysXYZ abc
+      else prettyRatioOfQSpraysX1X2X3 (abc !! 0)
+
+-- | Pretty form of a parametric rational spray
+prettyParametricQSpray :: ParametricQSpray -> String 
+prettyParametricQSpray = 
+  showSpray (prettyRatioOfQSpraysX1X2X3 "a") ("{ ", " }") (showMonomialsXYZ ["X", "Y", "Z"])
+
+-- | Pretty form of a simple parametric rational spray, using some given strings (typically some 
+-- letters) to denote the parameters and some given strings (typically some letters) to 
+-- denote the variables
+--
+-- >>> type SPQS = SimpleParametricQSpray
+-- >>> :{
+-- >>> f :: (QSpray, QSpray) -> (SPQS, SPQS, SPQS) -> SPQS
+-- >>> f (a, b) (x, y, z) = 
+-- >>>   (a ^+^ b) *^ x^**^2  ^+^  (a^**^2 ^+^ b^**^2) *^ (y ^*^ z)
+-- >>> :}
+-- >>> a = qlone 1
+-- >>> b = qlone 2
+-- >>> x = lone 1 :: SPQS
+-- >>> y = lone 2 :: SPQS
+-- >>> z = lone 3 :: SPQS
+-- >>> spqs = f (a, b) (x, y, z)
+-- >>> putStrLn $ prettySimpleParametricQSprayABCXYZ ["a","b"] ["X","Y","Z"] spqs
+-- { a + b }*X^2 + { a^2 + b^2 }*Y.Z
+prettySimpleParametricQSprayABCXYZ ::
+     [String]               -- ^ usually some letters, to denote the parameters of the spray
+  -> [String]               -- ^ usually some letters, to denote the variables of the spray
+  -> SimpleParametricQSpray -- ^ a parametric rational spray
+  -> String 
+prettySimpleParametricQSprayABCXYZ abc xyz spray = 
+  showSpray sprayShower ("{ ", " }") (showMonomialsXYZ xyz) spray
+  where
+    sprayShower = if numberOfParameters spray <= length abc
+      then prettyQSprayXYZ abc
+      else prettyQSprayX1X2X3 (abc !! 0)
+
+-- | Pretty form of a simple parametric rational spray
+prettySimpleParametricQSpray :: SimpleParametricQSpray -> String 
+prettySimpleParametricQSpray = 
+  showSpray (prettyQSprayX1X2X3 "a") ("{ ", " }") (showMonomialsXYZ ["X", "Y", "Z"]) 
tests/Main.hs view
@@ -4,6 +4,7 @@ import qualified Algebra.Ring                   as AlgRing      
 import qualified Algebra.Field                  as AlgField      
 import           Approx                         ( approx, assertApproxEqual )
+import qualified Data.HashMap.Strict            as HM
 import           Data.Matrix                    ( Matrix, fromLists )
 import           Data.Maybe                     ( fromJust )
 import           Data.Ratio                     ( (%) )
@@ -48,26 +49,26 @@                                                   sprayDivision,
                                                   gcdSpray,
                                                   QSpray',
-                                                  SymbolicQSpray,
+                                                  Rational',
+                                                  OneParameterQSpray,
                                                   evalRatioOfPolynomials,
-                                                  evalSymbolicSpray',
+                                                  evalOneParameterSpray',
                                                   qpolyFromCoeffs,
                                                   constQPoly,
-                                                  evalSymbolicSpray'',
+                                                  evalOneParameterSpray'',
                                                   prettyQSpray,
                                                   prettyQSprayX1X2X3,
                                                   prettySpray,
                                                   prettySpray'',
-                                                  outerQVariable,
+                                                  qsoleParameter,
                                                   constQPoly,
-                                                  prettySymbolicQSpray',
+                                                  prettyOneParameterQSpray',
                                                   (*.),
-                                                  gegenbauerPolynomial,
-                                                  evalSpraySpray,
                                                   RatioOfSprays (..),
                                                   RatioOfQSprays,
                                                   (%//%),
                                                   (%/%),
+                                                  (%:%),
                                                   unitRatioOfSprays,
                                                   isPolynomialRatioOfSprays,
                                                   evalRatioOfSprays,
@@ -75,12 +76,23 @@                                                   prettyRatioOfQSprays,
                                                   characteristicPolynomial,
                                                   detLaplace',
+                                                  gegenbauerPolynomial,
                                                   jacobiPolynomial,
                                                   asRatioOfSprays,
-                                                  ParametricQSpray, 
+                                                  SimpleParametricQSpray,
+                                                  ParametricQSpray,
+                                                  ParametricSpray,
                                                   zeroRatioOfSprays,
                                                   fromRatioOfQPolynomials,
-                                                  (^/^)
+                                                  HasVariables (..),
+                                                  numberOfParameters,
+                                                  changeParameters,
+                                                  substituteParameters, 
+                                                  evalParametricSpray,
+                                                  asSimpleParametricSpray,
+                                                  parametricSprayToOneParameterSpray,
+                                                  prettyParametricQSprayABCXYZ,
+                                                  asSimpleParametricSpray
                                                 )
 import           MathObj.Matrix                 ( fromRows )
 import qualified MathObj.Matrix                 as MathMatrix
@@ -94,16 +106,110 @@                                                 , testCase
                                                 )
 
+type PQS = ParametricQSpray
+
 main :: IO ()
 main = defaultMain $ testGroup
+
   "Testing hspray"
 
   [ 
-    testCase "fromRatioOfQPolynomials" $ do
+    testCase "asSimpleParametricSpray" $ do
       let
-        a = outerQVariable
+        jp = jacobiPolynomial 8
+        jp' = asSimpleParametricSpray jp
+        jp'' = HM.map asRatioOfSprays jp'
+      assertEqual "" jp jp''
+
+    , testCase "prettyParametricQSprayABCXYZ" $ do
+      let
+        f :: (QSpray, QSpray) -> (PQS, PQS, PQS) -> PQS
+        f (a, b) (x, y, z) = 
+          (a %:% (a ^+^ unitSpray)) *^ x^**^2  ^+^  (b %:% (a ^+^ b)) *^ (y ^*^ z)
+        pqs = f (lone 1, lone 2) (lone 1, lone 2, lone 3)
+        s1 = prettyParametricQSprayABCXYZ ["a","b"] ["X","Y","Z"] pqs
+        s2 = prettyParametricQSprayABCXYZ ["a"] ["X","Y","Z"] pqs
+        s3 = prettyParametricQSprayABCXYZ ["a","b"] ["X","Y"] pqs
+      assertEqual ""
+        [
+          s1, s2, s3
+        ]
+        [
+          "{ [ a ] %//% [ a + 1 ] }*X^2 + { [ b ] %//% [ a + b ] }*Y.Z",
+          "{ [ a1 ] %//% [ a1 + 1 ] }*X^2 + { [ a2 ] %//% [ a1 + a2 ] }*Y.Z",
+          "{ [ a ] %//% [ a + 1 ] }*X1^2 + { [ b ] %//% [ a + b ] }*X2.X3"
+        ]
+
+
+    , testCase "substituteParameters in Jacobi polynomial -> Legendre" $ do
+      let 
         x = qlone 1
-        rOP = ((a AlgRing.^ 8 AlgAdd.- AlgRing.one) ^/^ 
+        jacobi   = jacobiPolynomial 5
+        legendre = (63*^x^**^5 ^-^ 70*^x^**^3 ^+^ 15*^x) /^ 8 
+      assertEqual "" legendre (substituteParameters jacobi [0, 0])
+
+    , testCase "substituteParameters and evalParametricSpray" $ do
+      let 
+        jacobi  = jacobiPolynomial 5
+        jacobi' = substituteParameters jacobi [3, 2%7]
+        r1 = evaluate jacobi' [13]
+        r2 = evaluate (evalParametricSpray jacobi [13]) [3, 2%7] 
+      assertEqual "" r1 r2
+
+    , testCase "changeParameters in Jacobi polynomial -> Gegenbauer" $ do
+      let 
+        risingFactorial :: QSpray -> Int -> QSpray
+        risingFactorial theta n = 
+          AlgRing.product 
+            (map (\k -> theta ^+^ constantSpray (toRational k)) [0 .. n-1]) 
+        m = 5
+        alpha = qlone 1
+        jacobi = 
+          changeParameters (jacobiPolynomial m) 
+            [alpha ^-^ constantSpray (1%2), alpha ^-^ constantSpray (1%2)]
+        factor = 
+          risingFactorial (2 *^ alpha) m %//% 
+            risingFactorial (alpha ^+^ constantSpray (1%2)) m
+        obtained = asSimpleParametricSpray (factor *^ jacobi)
+        gegenbauer = gegenbauerPolynomial m
+      assertEqual "" gegenbauer obtained
+
+    , testCase "changeParameters in Jacobi polynomial" $ do
+      let 
+        n = 5
+        jp_n       = jacobiPolynomial n
+        jp_nminus1 = jacobiPolynomial (n-1)
+        jp_n'      = derivative 1 jp_n
+        a = qlone 1
+        b = qlone 2
+        spray = (a ^+^ b ^+^ constantSpray (toRational $ n + 1)) /^ 2
+        rOS = asRatioOfSprays spray
+        rhs = rOS *^ 
+          changeParameters jp_nminus1 [a ^+^ unitSpray, b ^+^ unitSpray]
+      assertEqual "" jp_n' rhs
+
+    , testCase "numberOfParameters in Jacobi polynomial" $ do
+      let 
+        jp = jacobiPolynomial 5
+      assertEqual "" (numberOfParameters jp) 2
+
+    , testCase "changeVariables in ratioOfSprays" $ do
+      let
+        f :: QSpray -> QSpray -> RatioOfQSprays
+        f p1 p2 = (p1^**^2 ^+^ 2 *^ p2) %//% (p1^**^3 ^-^ unitSpray)
+        x = qlone 1
+        y = qlone 2
+        rOS = f x y
+        u = x ^*^ y
+        v = x^**^2 ^-^ y ^+^ unitSpray 
+        rOS' = f u v 
+      assertEqual "" rOS' (changeVariables rOS [u, v])
+
+    , testCase "fromRatioOfQPolynomials" $ do
+      let
+        a = qsoleParameter
+        x = qlone 1
+        rOP = ((a AlgRing.^ 8 AlgAdd.- AlgRing.one) NR.% 
                                 (a AlgAdd.- AlgRing.one)) AlgRing.^ 3
               AlgAdd.+ (a AlgAdd.+ AlgRing.one) :% a 
         rOQ = ((x^**^8 ^-^ unitSpray) %//% (x ^-^ unitSpray)) AlgRing.^ 3 
@@ -133,7 +239,7 @@         beta0  = qlone 2
         cst :: Rational -> QSpray
         cst = constantSpray
-        x = lone 1 :: Spray RatioOfQSprays
+        x = lone 1 :: ParametricQSpray
         p = x ^-^ unitSpray
         t1 = asRatioOfSprays (((alpha0 ^+^ cst 1)^*^(alpha0 ^+^ cst 2)) /^ 2)
         t2 = asRatioOfSprays (((alpha0 ^+^ cst 2)^*^(alpha0 ^+^ beta0 ^+^ cst 3)) /^ 2) 
@@ -234,20 +340,20 @@         r = evalRatioOfSprays rOS values
       assertEqual "" r (f r1 r2)
 
-    , testCase "Gegenbauer" $ do
+    , testCase "Gegenbauer: differential equation and Chebyshev case" $ do
       let
         n = 5
         g   = gegenbauerPolynomial n
         g'  = derivative 1 g
         g'' = derivative 1 g'
-        alpha = lone 1 :: Spray Rational
-        x     = lone 1 :: Spray (Spray Rational)
+        alpha = lone 1 :: QSpray
+        x     = lone 1 :: SimpleParametricQSpray
         nAsSpray = constantSpray (toRational n)
         shouldBeZero = 
           (unitSpray ^-^ x^**^2) ^*^ g''
             ^-^ (2.^alpha ^+^ unitSpray) *^ (x ^*^ g')
               ^+^ n.^(nAsSpray ^+^ 2.^alpha) *^ g
-        chebyshev = fromRationalSpray $ evalSpraySpray g [1]
+        chebyshev = fromRationalSpray $ substituteParameters g [1]
         theta = 2.5
       assertEqual "" 
         (shouldBeZero, approx 8 $ sin theta * evalSpray chebyshev [cos theta]) 
@@ -622,11 +728,11 @@           where (f1, f2) = f px py pz
         (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)
+        (f1', f2')  = f (lone 1 :: OneParameterQSpray) (lone 2) (lone 3)
         symSpray  = rop1 *^ f1'  ^+^  rop2 *^ f2' 
-        r' = evalSymbolicSpray' symSpray 5 [2, 3, 4]
-        rop1' = evalSymbolicSpray'' f1' [2, 3]
-        rop2' = evalSymbolicSpray'' f2' [0, 0, 4]
+        r' = evalOneParameterSpray' symSpray 5 [2, 3, 4]
+        rop1' = evalOneParameterSpray'' f1' [2, 3]
+        rop2' = evalOneParameterSpray'' f2' [0, 0, 4]
         r'' = evalRatioOfPolynomials 5 (rop1 AlgRing.* rop1' AlgAdd.+ rop2 AlgRing.* rop2')
       assertEqual "" (r, r') (r', r''),
 
@@ -688,18 +794,37 @@           ]
       assertEqual "" strings strings',
 
-    testCase "prettySymbolicQSpray'" $ do
+    testCase "prettyOneParameterQSpray'" $ do
       let
-        x = lone 1 :: SymbolicQSpray 
-        y = lone 2 :: SymbolicQSpray 
-        z = lone 3 :: SymbolicQSpray 
-        a = outerQVariable  
+        x = lone 1 :: OneParameterQSpray 
+        y = lone 2 :: OneParameterQSpray 
+        z = lone 3 :: OneParameterQSpray 
+        a = qsoleParameter  
         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 = prettyOneParameterQSpray' "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'
+
+    , testCase "parametricSprayToOneParameterSpray" $ do
+      let 
+        x = lone 1 :: OneParameterQSpray 
+        y = lone 2 :: OneParameterQSpray 
+        z = lone 3 :: OneParameterQSpray 
+        a = qsoleParameter  
+        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)
+        x' = lone 1 :: ParametricSpray Rational'
+        y' = lone 2 :: ParametricSpray Rational'
+        z' = lone 3 :: ParametricSpray Rational'
+        a' = lone 1 :: Spray Rational'
+        spray  
+          = ((4 NR.% 5 :: Rational') AlgMod.*> (a' %:% (a'^**^2 ^+^ unitSpray))) *^ (x'^**^2 ^-^ y'^**^2)  
+            ^+^  ((2 NR.% 3) *^ a') AlgMod.*> (y' ^*^ z')
+      assertEqual "" (parametricSprayToOneParameterSpray spray) sSpray
+
 
   ]