# Multivariate series

AbstractAlgebra.jl provide multivariate series over a commutative ring.

Series with capped absolute precision are provided with and without weights.

For the unweighted case precision in each variable can be set per series, but is capped at some maximum precision which is set when defining the ring.

For the weighted case, a single precision is set on the ring only. Terms are truncated at that precision (after applying weights).

## Generic multivariate series

Generic multivariate series over a commutative ring, `AbsMSeries{T}`

is implemented in `src/generic/AbsMSeries.jl`

.

Such series are capped absolute series and have type `Generic.AbsMSeries{T}`

where `T`

is the type of elements of the coefficient ring.

Internally they consist of a multivariate polynomial. For unweighted series they also contain a vector of precisions, one for each variable.

For weighted series weights and a precision are stored on the ring only. The vector of precisions in the series objects is ignored.

See the file `src/generic/GenericTypes.jl`

for details of the type.

The series are implemented in terms of multivariate polynomials which are used internally to keep track of the coefficients of the series.

Only lex ordering is provided at present both weighted and unweighted, though series print in reverse order to what multivariate polynomials would print, i.e. least significant term first, as would be expected for series.

Parent objects of such series have type `Generic.AbsMSeriesRing{T}`

.

The symbol representation of the variables and the multivariate polynomial ring is stored in the parent object.

## Abstract types

Multivariate series element types belong to the abstract type `MSeriesElem{T}`

and the multivariate series ring types belong to the abstract type `MSeriesRing{T}`

. This enables one to write generic functions that can accept any AbstractAlgebra multivariate series type.

## Multivariate series ring constructors

In order to construct multivariate series in AbstractAlgebra.jl, one must first construct the series ring itself. This is accomplished with the following constructors.

For the unweighted case:

`power_series_ring(R::Ring, prec::Vector{Int}, s::AbstractVector{<:VarName}; cached::Bool = true)`

Given a base ring `R`

and a vector of strings `s`

specifying how the generators (variables) should be printed, along with a vector of precisions, one for each variable, return a tuple `U, (x, y, ...)`

representing the new series ring $S$ and the generators $x, y, \ldots$ of the ring as a tuple. By default the parent object `S`

will depend on `R`

, the precision vector and the variable names `x, y, ...`

and will be cached. Setting the optional argument `cached`

to `false`

will prevent the parent object `S`

from being cached.

In the weighted case:

`power_series_ring(R::Ring, weights::Vector{Int}, s::AbstractVector{<:VarName}, prec::Int; cached::Bool = true)`

Given a base ring `R`

and a vector of strings `s`

specifying how the generators (variables) should be printed, along with a vector of weights, one for each variable and a bound on the (weighted) precision, return a tuple `U, (x, y, ...)`

representing the new series ring $S$ and the generators $x, y, \ldots$ of the ring as a tuple. By default the parent object `S`

will depend on `R`

, the precision, the vector of weights and the variable names `x, y, ...`

and will be cached. Setting the optional argument `cached`

to `false`

will prevent the parent object `S`

from being cached.

Here are some examples of creating multivariate series rings and making use of the resulting parent objects to coerce various elements into the series ring.

Note that one can also use the function call `O(x^n)`

with unweighted series to specify the precision in the variable `x`

of a given series expression should be precision `n`

.

It is not possible to use `x^0`

in the `O()`

function, since there is no distinction between `x^0`

and `y^0`

as far as the system is concerned. If one wishes to set the precision of a variable to precision `0`

, one must use the `set_precision!`

function described below.

If one wants a series with the same precision in all variables, one can use `O(R, n)`

where `R`

is the series ring and `n`

is the desired precision.

If all the precisions are to be the same, the vector of integers for the precisions can be replaced by a single integer in the constructor.

**Examples**

```
julia> R, (x, y) = power_series_ring(ZZ, [2, 3], [:x, :y])
(Multivariate power series ring in 2 variables over integers, AbstractAlgebra.Generic.AbsMSeries{BigInt, AbstractAlgebra.Generic.MPoly{BigInt}}[x + O(y^3) + O(x^2), y + O(y^3) + O(x^2)])
julia> f = R()
O(y^3) + O(x^2)
julia> g = R(123)
123 + O(y^3) + O(x^2)
julia> h = R(BigInt(1234))
1234 + O(y^3) + O(x^2)
julia> k = R(x + 1)
1 + x + O(y^3) + O(x^2)
julia> m = x + y + O(y^2)
y + x + O(y^2) + O(x^2)
julia> R, (x, y) = power_series_ring(ZZ, 3, [:x, :y])
(Multivariate power series ring in 2 variables over integers, AbstractAlgebra.Generic.AbsMSeries{BigInt, AbstractAlgebra.Generic.MPoly{BigInt}}[x + O(y^3) + O(x^3), y + O(y^3) + O(x^3)])
julia> n = x + y + O(R, 2)
y + x + O(y^2) + O(x^2)
julia> R, (x, y) = power_series_ring(ZZ, [2, 3], 10, [:x, :y])
(Multivariate power series ring in 2 variables over integers, AbstractAlgebra.Generic.AbsMSeries{BigInt, AbstractAlgebra.Generic.MPoly{BigInt}}[x + O(10), y + O(10)])
julia> R()
O(10)
julia> R(x)
x + O(10)
```

## Basic ring functionality

Once a multivariate series ring is constructed, there are various ways to construct series in that ring.

The easiest way is simply using the generators returned by the `power_series_ring`

constructor and build up the power series using basic arithmetic, as described in the Ring interface.

The power series rings in AbstractAlgebra.jl implement the full Ring interface.

We give some examples of such functionality.

The divexact function can currently only divide by unit series (i.e. whose constant coefficient is invertible).

**Examples**

```
julia> R, (x,) = power_series_ring(ZZ, [5], [:x])
(Multivariate power series ring in 1 variable over integers, AbstractAlgebra.Generic.AbsMSeries{BigInt, AbstractAlgebra.Generic.MPoly{BigInt}}[x + O(x^5)])
julia> f = x^3 + 3x + 21
21 + 3*x + x^3 + O(x^5)
julia> h = zero(R)
O(x^5)
julia> k = one(R)
1 + O(x^5)
julia> isone(k)
true
julia> iszero(f)
false
julia> n = length(f)
3
julia> U = base_ring(R)
Integers
julia> v = symbols(R)
1-element Vector{Symbol}:
:x
julia> T = parent(x + 1)
Multivariate power series ring in 1 variable x
over integers
julia> f == deepcopy(f)
true
julia> t = divexact(f*x, 1 + x)
21*x - 18*x^2 + 18*x^3 - 17*x^4 + O(x^5)
julia> R, (x, y) = power_series_ring(ZZ, [2, 3], 10, [:x, :y])
(Multivariate power series ring in 2 variables over integers, AbstractAlgebra.Generic.AbsMSeries{BigInt, AbstractAlgebra.Generic.MPoly{BigInt}}[x + O(10), y + O(10)])
julia> f = 3x^2*y + 1
1 + 3*y*x^2 + O(10)
julia> one(R)
1 + O(10)
```

## Power series functionality provided by AbstractAlgebra.jl

The functionality listed below is automatically provided by AbstractAlgebra.jl for absolute series over any commutative ring.

### Basic functionality

The following are provided for weighted and unweighted series:

`AbstractAlgebra.number_of_variables`

— Method`number_of_variables(R::AbsMSeriesRing)`

Return the number of variables in the series ring.

`AbstractAlgebra.symbols`

— Method`symbols(R::MSeriesRing)`

Return a vector of symbols, one for each of the variables of the series ring $R$.

`Base.precision`

— Method`precision(a::AbsMSeries)`

Return a vector of precisions, one for each variable in the series ring. If the ring is weighted the weighted precision is returned instead.

`AbstractAlgebra.coeff`

— Method`coeff(a::AbsMSeries, n::Int)`

Return the coefficient of the $n$-th nonzero term of the series (or zero if there are fewer than $n$ nonzero terms). Terms are numbered from the least significant term, i.e. the first term displayed when the series is printed.

`AbstractAlgebra.characteristic`

— Method`characteristic(R::FracField{T}) where T <: RingElem`

Return the characteristic of the given field.

`AbstractAlgebra.gen`

— Method`gen(R::AbsMSeriesRing, i::Int)`

Return the $i$-th generator (variable) of the series ring $R$. Numbering starts from $1$ for the most significant variable.

`AbstractAlgebra.gens`

— Method`gens(R::AbsMSeriesRing)`

Return a vector of the generators (variables) of the series ring $R$, starting with the most significant.

`AbstractAlgebra.is_gen`

— Method`is_gen(a::AbsMSeries)`

Return true if the series $a$ is a generator of its parent series ring.

`AbstractAlgebra.is_unit`

— Method`is_unit(a::AbsMSeries)`

Return `true`

if the series is a unit in its series ring, i.e. if its constant term is a unit in the base ring.

`Base.length`

— Method`length(a::AbsMSeries)`

Return the number of nonzero terms in the series $a$.

The following are only available for unweighted series.

`AbstractAlgebra.max_precision`

— Method`max_precision(R::AbsMSeriesRing)`

Return a vector of precision caps, one for each variable in the ring. Arithmetic operations will be performed to precisions not exceeding these values.

`AbstractAlgebra.valuation`

— Method`valuation(a::AbsMSeries)`

Return the valuation of $a$ as a vector of integers, one for each variable.

### Iteration

`AbstractAlgebra.coefficients`

— Method`coefficients(a::AbsMSeries)`

Return an array of the nonzero coefficients of the series, in the order they would be displayed, i.e. least significant term first.

`AbstractAlgebra.exponent_vectors`

— Method`exponent_vectors(a::AbsMSeries)`

Return an array of the exponent vectors of the nonzero terms of the series, in the order they would be displayed, i.e. least significant term first.

### Truncation

`Base.truncate`

— Method`truncate(a::AbstractAlgebra.AbsMSeries, prec::Vector{Int})`

Return $a$ truncated to (absolute) precisions given by the vector `prec`

.

`Base.truncate`

— Method`truncate(a::AbstractAlgebra.AbsMSeries, prec::Int)`

Return $a$ truncated to precision `prec`

. This either truncates by weight in the weighted cases or truncates each variable to precision `prec`

in the unweighted case.

### Exact division

`AbstractAlgebra.divexact`

— Method`divexact(x::AbsMSeries{T}, y::AbsMSeries{T}; check::Bool=true) where T <: RingElement`

Return the exact quotient of the series $x$ by the series $y$. This function currently assumes $y$ is an invertible series.

### Evaluation

`AbstractAlgebra.evaluate`

— Method`evaluate(a::U, vars::Vector{Int}, vals::Vector{U}) where {T <: RingElement, U <: AbsMSeries{T}}`

Evaluate the series expression by substituting in the supplied values in the array `vals`

for the corresponding variables with indices given by the array `vars`

. The values must be in the same ring as $a$.

`AbstractAlgebra.evaluate`

— Method`evaluate(a::U, vars::Vector{U}, vals::Vector{U}) where {T <: RingElement, U <: AbsMSeries{T}}`

Evaluate the series expression by substituting in the supplied values in the array `vals`

for the corresponding variables given by the array `vars`

. The values must be in the same ring as $a$.

`AbstractAlgebra.evaluate`

— Method`evaluate(a::U, vals::Vector{U}) where {T <: RingElement, U <: AbsMSeries{T}}`

Evaluate the series expression by substituting in the supplied values in the array `vals`

for the variables the series ring to which $a$ belongs. The values must be in the same ring as $a$.

### Random generation

`Base.rand`

— Method`rand(S::MSeriesRing, term_range, v...)`

Return a random element of the series ring $S$ with number of terms in the range given by `term_range`

and where coefficients of the series are randomly generated in the base ring using the data given by `v`

. The exponents of the variable in the terms will be less than the precision caps for the Ring $S$ when it was created.