```@meta
CurrentModule = AbstractAlgebra
CollapsedDocStrings = true
DocTestSetup = AbstractAlgebra.doctestsetup()
```

# Generic residue rings

AbstractAlgebra.jl provides modules, implemented in `src/Residue.jl` and
`src/residue_field` for residue rings and fields, respectively, over any
Euclidean domain (in practice most of the functionality is provided for GCD
domains that provide a meaningful GCD function) belonging to the
AbstractAlgebra.jl abstract type hierarchy.

## Generic residue types

AbstractAlgebra.jl implements generic residue rings of Euclidean rings with type `EuclideanRingResidueRingElem{T}`
or in the case of residue rings that are known to be fields, `EuclideanRingResidueFieldElem{T}`,
where `T` is the type of elements of the base ring. See the file
`src/generic/GenericTypes.jl` for details.

Parent objects of generic residue ring elements have type `EuclideanRingResidueRing{T}`
and those of residue fields have type `EuclideanRingResidueField{T}`.

The defining modulus of the residue ring is stored in the parent object.

## Abstract types

All residue element types belong to the abstract type `ResElem{T}`
or `ResFieldElem{T}` in the case of residue fields, and the
residue ring types belong to the abstract type `ResidueRing{T}` or `ResidueField{T}`
respectively. This enables one to write generic functions that can accept any
AbstractAlgebra residue type.

## Residue ring constructors

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

```julia
residue_ring(R::Ring, m::RingElem; cached::Bool = true)
```
```julia
residue_field(R::Ring, m::RingElem; cached::Bool = true)
```

Given a base ring `R` and residue $m$ contained in this ring, return the parent object
of the residue ring $R/(m)$ together with the canonical projection. By default
the parent object `S` will depend only on `R` and `m` and will be cached.
Setting the optional argument `cached` to `false` will
prevent the parent object `S` from being cached.

The `residue_field` constructor does the same thing as the `residue_ring` constructor,
but the resulting object has type belonging to `Field` rather than `Ring`, so it can
be used anywhere a field is expected in AbstractAlgebra.jl. No check is made for
maximality of the ideal generated by $m$.

There are also the following for constructing residue rings and fields.

```@docs
quo(R::Ring, m::RingElem; cached::Bool = true)
quo(::Type{Field}, R::Ring, m::RingElem; cached::Bool = true)
```

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

**Examples**

```jldoctest
julia> R, x = polynomial_ring(QQ, :x)
(Univariate polynomial ring in x over rationals, x)

julia> S, = residue_ring(R, x^3 + 3x + 1);

julia> f = S()
0

julia> g = S(123)
123

julia> h = S(BigInt(1234))
1234

julia> k = S(x + 1)
x + 1

julia> U, f = quo(R, x^3 + 3x + 1)
(Residue ring of R modulo x^3 + 3*x + 1, Map: R -> S)

julia> U === S
true
```

All of the examples here are generic residue rings, but specialised implementations
of residue rings provided by external modules will also usually provide a
`residue_ring` constructor to allow creation of their residue rings.

## Residue constructors

One can use the parent objects of a residue ring to construct residues, as per any
ring.

```julia
(R::ResidueRing)() # constructs zero
(R::ResidueRing)(c::Integer)
(R::ResidueRing)(c::elem_type(R))
(R::ResidueRing{T})(a::T) where T <: RingElement
```

## Functions for types and parents of residue rings

```julia
base_ring(R::ResidueRing)
base_ring(a::ResElem)
```

Return the base ring over which the ring was constructed.

```julia
parent(a::ResElem)
```

Return the parent of the given residue.

## Residue ring functions

### Basic functionality

Residue rings implement the Ring interface.

```julia
zero(R::NCRing)
one(R::NCRing)
iszero(a::NCRingElement)
isone(a::NCRingElement)
```

```julia
divexact(a::T, b::T) where T <: RingElement
inv(a::T)
```

The Residue Ring interface is also implemented.

```julia
modulus(S::ResidueRing)
```

```julia
data(f::ResElem)
lift(f::ResElem)
```

Return a lift of the residue to the base ring.

The following functions are also provided for residues.

```@docs
modulus(::ResElem)
```

**Examples**

```jldoctest
julia> R, x = polynomial_ring(QQ, :x)
(Univariate polynomial ring in x over rationals, x)

julia> S, = residue_ring(R, x^3 + 3x + 1);

julia> f = S(x + 1)
x + 1

julia> h = zero(S)
0

julia> k = one(S)
1

julia> isone(k)
true

julia> iszero(f)
false

julia> is_unit(f)
true

julia> m = modulus(S)
x^3 + 3*x + 1

julia> d = data(f)
x + 1

julia> U = base_ring(S)
Univariate polynomial ring in x over rationals

julia> V = base_ring(f)
Univariate polynomial ring in x over rationals

julia> T = parent(f)
Residue ring of R modulo x^3 + 3*x + 1

julia> f == deepcopy(f)
true

julia> R, x = polynomial_ring(QQ, :x)
(Univariate polynomial ring in x over rationals, x)
```

### Inversion

```@docs
Base.inv(::ResElem)
```

**Examples**

```jldoctest
julia> R, x = polynomial_ring(QQ, :x)
(Univariate polynomial ring in x over rationals, x)

julia> S, = residue_ring(R, x^3 + 3x + 1);

julia> f = S(x + 1)
x + 1

julia> g = inv(f)
1//3*x^2 - 1//3*x + 4//3

```

### Greatest common divisor

```@docs
gcd(::ResElem{T}, ::ResElem{T}) where T <: RingElem
```

**Examples**

```jldoctest
julia> R, x = polynomial_ring(QQ, :x)
(Univariate polynomial ring in x over rationals, x)

julia> S, = residue_ring(R, x^3 + 3x + 1);

julia> f = S(x + 1)
x + 1

julia> g = S(x^2 + 2x + 1)
x^2 + 2*x + 1

julia> h = gcd(f, g)
1

```

### Square Root

Methods for `is_square` and `sqrt` are provided for inputs of type `ResFieldElem`.

**Examples**

```julia
julia> R = residue_field(ZZ, 733)
Residue field of Integers modulo 733

julia> a = R(86)
86

julia> is_square(a)
true

julia> sqrt(a)
532
```

### Random generation

Random residues can be generated using `rand`. The parameters after the residue
ring are used to generate elements of the base ring.

```julia
rand(R::ResidueRing, v...)
```

**Examples**

```jldoctest; setup = :(import Random; Random.seed!(42))
julia> R, = residue_ring(ZZ, 7);

julia> f = rand(R, 0:6)
4

julia> S, x = polynomial_ring(QQ, :x)
(Univariate polynomial ring in x over rationals, x)

julia> g = rand(S, 2:2, -10:10)
-1//4*x^2 - 2//7*x + 1
```
