This tutorial outlines what you need to know in order to use \(p\)-adics in Sage effectively.

Our goal is to create a rich structure of different options that will reflect the mathematical structures of the \(p\)-adics. This is very much a work in progress: some of the classes that we eventually intend to include have not yet been written, and some of the functionality for classes in existence has not yet been implemented. In addition, while we strive for perfect code, bugs (both subtle and not-so-subtle) continue to evade our clutches. As a user, you serve an important role. By writing non-trivial code that uses the \(p\)-adics, you both give us insight into what features are actually used and also expose problems in the code for us to fix.

Our design philosophy has been to create a robust, usable interface
working first, with simple-minded implementations underneath. We
want this interface to stabilize rapidly, so that users’ code does
not have to change. Once we get the framework in place, we can go
back and work on the algorithms and implementations underneath. All
of the current \(p\)-adic code is currently written in pure
Python, which means that it does not have the speed advantage of
compiled code. Thus our \(p\)-adics can be painfully slow at
times when you’re doing real computations. However, finding and
fixing bugs in Python code is *far* easier than finding and fixing
errors in the compiled alternative within Sage (Cython), and Python
code is also faster and easier to write. We thus have significantly
more functionality implemented and working than we would have if we
had chosen to focus initially on speed. And at some point in the
future, we will go back and improve the speed. Any code you have
written on top of our \(p\)-adics will then get an immediate
performance enhancement.

If you do find bugs, have feature requests or general comments, please email sage-support@groups.google.com or roed@math.harvard.edu.

To write down a general \(p\)-adic element completely would require an infinite amount of data. Since computers do not have infinite storage space, we must instead store finite approximations to elements. Thus, just as in the case of floating point numbers for representing reals, we have to store an element to a finite precision level. The different ways of doing this account for the different types of \(p\)-adics.

We can think of \(p\)-adics in two ways. First, as a projective limit of finite groups:

\[\mathbb{Z}_p = \lim_{\leftarrow n} \mathbb{Z}/p^n\mathbb{Z}.\]

Secondly, as Cauchy sequences of rationals (or integers, in the case of \(\mathbb{Z}_p\)) under the \(p\)-adic metric. Since we only need to consider these sequences up to equivalence, this second way of thinking of the \(p\)-adics is the same as considering power series in \(p\) with integral coefficients in the range \(0\) to \(p-1\). If we only allow nonnegative powers of \(p\) then these power series converge to elements of \(\mathbb{Z}_p\), and if we allow bounded negative powers of \(p\) then we get \(\mathbb{Q}_p\).

Both of these representations give a natural way of thinking about finite approximations to a \(p\)-adic element. In the first representation, we can just stop at some point in the projective limit, giving an element of \(\mathbb{Z}/p^n\mathbb{Z}\). As \(\mathbb{Z}_p / p^n\mathbb{Z}_p \cong \mathbb{Z}/p^n\mathbb{Z}\), this is equivalent to specifying our element modulo \(p^n\mathbb{Z}_p\).

The *absolute precision* of a finite approximation
\(\bar{x} \in \mathbb{Z}/p^n\mathbb{Z}\) to \(x \in \mathbb{Z}_p\)
is the non-negative integer \(n\).

In the second representation, we can achieve the same thing by truncating a series

\[a_0 + a_1 p + a_2 p^2 + \cdots\]

at \(p^n\), yielding

\[a_0 + a_1 p + \cdots + a_{n-1} p^{n-1} + O(p^n).\]

As above, we call this \(n\) the absolute precision of our element.

Given any \(x \in \mathbb{Q}_p\) with \(x \ne 0\), we
can write \(x = p^v u\) where \(v \in \ZZ\) and
\(u \in \mathbb{Z}_p^{\times}\). We could thus also store an element
of \(\mathbb{Q}_p\) (or \(\mathbb{Z}_p\)) by storing
\(v\) and a finite approximation of \(u\). This
motivates the following definition: the *relative precision* of an
approximation to \(x\) is defined as the absolute precision
of the approximation minus the valuation of \(x\). For
example, if
\(x = a_k p^k + a_{k+1} p^{k+1} +
\cdots + a_{n-1} p^{n-1} + O(p^n)\)
then the absolute precision of \(x\) is \(n\), the
valuation of \(x\) is \(k\) and the relative precision
of \(x\) is \(n-k\).

There are three different representations of \(\mathbb{Z}_p\) in Sage and one representation of \(\mathbb{Q}_p\):

- the fixed modulus ring
- the capped absolute precision ring
- the capped relative precision ring, and
- the capped relative precision field.

The first, and simplest, type of \(\mathbb{Z}_p\) is basically a wrapper around \(\mathbb{Z}/p^n\mathbb{Z}\), providing a unified interface with the rest of the \(p\)-adics. You specify a precision, and all elements are stored to that absolute precision. If you perform an operation that would normally lose precision, the element does not track that it no longer has full precision.

The fixed modulus ring provides the lowest level of convenience, but it is also the one that has the lowest computational overhead. Once we have ironed out some bugs, the fixed modulus elements will be those most optimized for speed.

As with all of the implementations of \(\mathbb{Z}_p\), one
creates a new ring using the constructor `Zp`, and passing in
`'fixed-mod'` for the `type` parameter. For example,

```
sage: R = Zp(5, prec = 10, type = 'fixed-mod', print_mode = 'series')
sage: R
5-adic Ring of fixed modulus 5^10
```

One can create elements as follows:

```
sage: a = R(375)
sage: a
3*5^3 + O(5^10)
sage: b = R(105)
sage: b
5 + 4*5^2 + O(5^10)
```

Now that we have some elements, we can do arithmetic in the ring.

```
sage: a + b
5 + 4*5^2 + 3*5^3 + O(5^10)
sage: a * b
3*5^4 + 2*5^5 + 2*5^6 + O(5^10)
```

Floor division (//) divides even though the result isn’t really known to the claimed precision; note that division isn’t defined:

```
sage: a // 5
3*5^2 + O(5^10)
```

```
sage: a / 5
Traceback (most recent call last):
...
ValueError: cannot invert non-unit
```

Since elements don’t actually store their actual precision, one can only divide by units:

```
sage: a / 2
4*5^3 + 2*5^4 + 2*5^5 + 2*5^6 + 2*5^7 + 2*5^8 + 2*5^9 + O(5^10)
sage: a / b
Traceback (most recent call last):
...
ValueError: cannot invert non-unit
```

If you want to divide by a non-unit, do it using the `//`
operator:

```
sage: a // b
3*5^2 + 3*5^3 + 2*5^5 + 5^6 + 4*5^7 + 2*5^8 + O(5^10)
```

The second type of implementation of \(\mathbb{Z}_p\) is similar to the fixed modulus implementation, except that individual elements track their known precision. The absolute precision of each element is limited to be less than the precision cap of the ring, even if mathematically the precision of the element would be known to greater precision (see Appendix A for the reasons for the existence of a precision cap).

Once again, use `Zp` to create a capped absolute \(p\)-adic
ring.

```
sage: R = Zp(5, prec = 10, type = 'capped-abs', print_mode = 'series')
sage: R
5-adic Ring with capped absolute precision 10
```

We can do similar things as in the fixed modulus case:

```
sage: a = R(375)
sage: a
3*5^3 + O(5^10)
sage: b = R(105)
sage: b
5 + 4*5^2 + O(5^10)
sage: a + b
5 + 4*5^2 + 3*5^3 + O(5^10)
sage: a * b
3*5^4 + 2*5^5 + 2*5^6 + O(5^10)
sage: c = a // 5
sage: c
3*5^2 + O(5^9)
```

Note that when we divided by 5, the precision of `c` dropped.
This lower precision is now reflected in arithmetic.

```
sage: c + b
5 + 2*5^2 + 5^3 + O(5^9)
```

Division is allowed: the element that results is a capped relative field element, which is discussed in the next section:

```
sage: 1 / (c + b)
5^-1 + 3 + 2*5 + 5^2 + 4*5^3 + 4*5^4 + 3*5^6 + O(5^7)
```

Instead of restricting the absolute precision of elements (which doesn’t make much sense when elements have negative valuations), one can cap the relative precision of elements. This is analogous to floating point representations of real numbers. As in the reals, multiplication works very well: the valuations add and the relative precision of the product is the minimum of the relative precisions of the inputs. Addition, however, faces similar issues as floating point addition: relative precision is lost when lower order terms cancel.

To create a capped relative precision ring, use `Zp` as before.
To create capped relative precision fields, use `Qp`.

```
sage: R = Zp(5, prec = 10, type = 'capped-rel', print_mode = 'series')
sage: R
5-adic Ring with capped relative precision 10
sage: K = Qp(5, prec = 10, type = 'capped-rel', print_mode = 'series')
sage: K
5-adic Field with capped relative precision 10
```

We can do all of the same operations as in the other two cases, but precision works a bit differently: the maximum precision of an element is limited by the precision cap of the ring.

```
sage: a = R(375)
sage: a
3*5^3 + O(5^13)
sage: b = K(105)
sage: b
5 + 4*5^2 + O(5^11)
sage: a + b
5 + 4*5^2 + 3*5^3 + O(5^11)
sage: a * b
3*5^4 + 2*5^5 + 2*5^6 + O(5^14)
sage: c = a // 5
sage: c
3*5^2 + O(5^12)
sage: c + 1
1 + 3*5^2 + O(5^10)
```

As with the capped absolute precision rings, we can divide, yielding a capped relative precision field element.

```
sage: 1 / (c + b)
5^-1 + 3 + 2*5 + 5^2 + 4*5^3 + 4*5^4 + 3*5^6 + 2*5^7 + 5^8 + O(5^9)
```

One can create unramified extensions of \(\mathbb{Z}_p\) and
\(\mathbb{Q}_p\) using the functions `Zq` and `Qq`.

In addition to requiring a prime power as the first argument,
`Zq` also requires a name for the generator of the residue field.
One can specify this name as follows:

```
sage: R.<c> = Zq(125, prec = 20); R
Unramified Extension of 5-adic Ring with capped absolute precision 20
in c defined by (1 + O(5^20))*x^3 + (O(5^20))*x^2 + (3 + O(5^20))*x + (3 + O(5^20))
```

It is also possible to create Eisenstein extensions of \(\mathbb{Z}_p\) and \(\mathbb{Q}_p\). In order to do so, create the ground field first:

```
sage: R = Zp(5, 2)
```

Then define the polynomial yielding the desired extension.:

```
sage: S.<x> = ZZ[]
sage: f = x^5 - 25*x^3 + 15*x - 5
```

Finally, use the `ext` function on the ground field to create the
desired extension.:

```
sage: W.<w> = R.ext(f)
```

You can do arithmetic in this Eisenstein extension:

```
sage: (1 + w)^7
1 + 2*w + w^2 + w^5 + 3*w^6 + 3*w^7 + 3*w^8 + w^9 + O(w^10)
```

Note that the precision cap increased by a factor of 5, since the ramification index of this extension over \(\mathbb{Z}_p\) is 5.