Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
""" p-Adic Printing
This file contains code for printing p-adic elements.
It has been moved here to prevent code duplication and make finding the relevant code easier.
AUTHORS:
- David Roe """
#***************************************************************************** # Copyright (C) 2008-2013 David Roe <roed.math@gmail.com> # William Stein <wstein@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function, absolute_import
from cpython.list cimport * from sage.libs.gmp.mpz cimport * from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool from sage.rings.padics.misc import trim_zeros
import sys
from sage.rings.integer cimport Integer
cdef enum print_modes: terse series val_unit digits bars
def pAdicPrinter(ring, options={}): """ Creates a pAdicPrinter.
INPUT:
- ring -- a p-adic ring or field.
- options -- a dictionary, with keys in 'mode', 'pos', 'ram_name', 'unram_name', 'var_name', 'max_ram_terms', 'max_unram_terms', 'max_terse_terms', 'sep', 'alphabet'; see pAdicPrinter_class for the meanings of these keywords.
EXAMPLES::
sage: from sage.rings.padics.padic_printing import pAdicPrinter sage: R = Zp(5) sage: pAdicPrinter(R, {'sep': '&'}) series printer for 5-adic Ring with capped relative precision 20 """
class pAdicPrinterDefaults(SageObject): """ This class stores global defaults for p-adic printing. """ def __init__(self, mode = 'series', pos = True, max_ram_terms = -1, max_unram_terms = -1, max_terse_terms = -1, sep = "|", alphabet = None): """ Instances of this class store global defaults used in determining printing options during the creation of p-adic rings and fields. One instance stored in padic_printing stores the globally relevant default values.
See pAdicPrinter_class for details on the meanings of these inputs.
TESTS::
sage: from sage.rings.padics.padic_printing import pAdicPrinterDefaults sage: D = pAdicPrinterDefaults(sep='&'); D.sep() '&' """ raise ValueError("max_ram_terms must be positive and fit in a long") raise ValueError("max_unram_terms must be positive and fit in a long") raise ValueError("max_terse_terms must be positive and fit in a long") else: self._alphabet = alphabet
def mode(self, mode=None): """ Set the default printing mode.
mode=None returns the current value.
The allowed values for mode are: 'val-unit', 'series', 'terse', 'digits' and 'bars'.
EXAMPLES::
sage: padic_printing.mode('terse') sage: padic_printing.mode() 'terse' sage: Qp(7)(100) 100 + O(7^20) sage: padic_printing.mode('series') sage: Qp(11)(100) 1 + 9*11 + O(11^20) sage: padic_printing.mode('val-unit') sage: Qp(13)(130) 13 * 10 + O(13^21) sage: padic_printing.mode('digits') sage: repr(Qp(17)(100)) '...5F' sage: repr(Qp(17)(1000)) '...37E' sage: padic_printing.mode('bars') sage: repr(Qp(19)(1000)) '...2|14|12'
sage: padic_printing.mode('series') """ else: else: raise ValueError("invalid printing mode")
def allow_negatives(self, neg = None): """ Controls whether or not to display a balanced representation.
neg=None returns the current value.
EXAMPLES::
sage: padic_printing.allow_negatives(True) sage: padic_printing.allow_negatives() True sage: Qp(29)(-1) -1 + O(29^20) sage: Qp(29)(-1000) -14 - 5*29 - 29^2 + O(29^20) sage: padic_printing.allow_negatives(False) """ else:
def max_series_terms(self, max = None): """ Controls the maximum number of terms shown when printing in 'series', 'digits' or 'bars' mode.
max=None returns the current value.
max=-1 encodes 'no limit.'
EXAMPLES::
sage: padic_printing.max_series_terms(2) sage: padic_printing.max_series_terms() 2 sage: Qp(31)(1000) 8 + 31 + ... + O(31^20) sage: padic_printing.max_series_terms(-1) sage: Qp(37)(100000) 26 + 37 + 36*37^2 + 37^3 + O(37^20) """ else:
def max_unram_terms(self, max = None): """ For rings with non-prime residue fields, controls how many terms appear in the coefficient of each pi^n when printing in 'series' or 'bar' modes.
max=None returns the current value.
max=-1 encodes 'no limit.'
EXAMPLES::
sage: padic_printing.max_unram_terms(2) sage: padic_printing.max_unram_terms() 2 sage: Zq(5^6, 5, names='a')([1,2,3,-1])^17 (3*a^4 + ... + 3) + (a^5 + ... + a)*5 + (3*a^3 + ... + 2)*5^2 + (3*a^5 + ... + 2)*5^3 + (4*a^5 + ... + 4)*5^4 + O(5^5)
sage: padic_printing.max_unram_terms(-1) """ else:
def max_poly_terms(self, max = None): """ Controls the number of terms appearing when printing polynomial representations in 'terse' or 'val-unit' modes.
max=None returns the current value.
max=-1 encodes 'no limit.'
EXAMPLES::
sage: padic_printing.max_poly_terms(3) sage: padic_printing.max_poly_terms() 3 sage: padic_printing.mode('terse') sage: Zq(7^5, 5, names='a')([2,3,4])^8 2570 + 15808*a + 9018*a^2 + ... + O(7^5)
sage: padic_printing.max_poly_terms(-1) sage: padic_printing.mode('series') """ else:
def sep(self, sep = None): """ Controls the separator used in 'bars' mode.
sep=None returns the current value.
EXAMPLES::
sage: padic_printing.sep('][') sage: padic_printing.sep() '][' sage: padic_printing.mode('bars') sage: repr(Qp(61)(-1)) '...60][60][60][60][60][60][60][60][60][60][60][60][60][60][60][60][60][60][60][60'
sage: padic_printing.sep('|') sage: padic_printing.mode('series') """ else:
def alphabet(self, alphabet = None): """ Controls the alphabet used to translate p-adic digits into strings (so that no separator need be used in 'digits' mode).
alphabet should be passed in as a list or tuple.
alphabet=None returns the current value.
EXAMPLES::
sage: padic_printing.alphabet("abc") sage: padic_printing.mode('digits') sage: repr(Qp(3)(1234)) '...bcaacab'
sage: padic_printing.mode('series') sage: padic_printing.alphabet(('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z')) """ else:
_printer_defaults = pAdicPrinterDefaults()
cdef class pAdicPrinter_class(SageObject): """ This class stores the printing options for a specific p-adic ring or field, and uses these to compute the representations of elements. """ def __init__(self, ring, mode, pos, ram_name, unram_name, var_name, max_ram_terms, max_unram_terms, max_terse_terms, sep, alphabet, show_prec): """ Initializes a pAdicPrinter.
INPUT:
- ring -- the ring or field to which this pAdicPrinter is attached.
- mode -- The allowed values for mode are: 'val-unit', 'series', 'terse', 'digits' and 'bars'.
- 'val-unit' -- elements are displayed as a power of the uniformizer times a unit, which is displayed in terse mode.
- 'series' -- elements are displayed as power series in the uniformizer.
- 'terse' -- for base rings and fields, elements are just displayed as an integer lift. For extensions rings and fields, elements are displayed as a polynomial in the generator of the extension.
- 'digits' -- Used only for small primes and totally ramified extensions (or trivial extensions), elements are displayed as just a string of p-adic digits, encoded using the 'alphabet' parameter.
- 'bars' -- Like 'digits', but uses a separator in order to print a more canonical representation for each digit. This change allows the use of this printing mode for unramified extensions and extensions with larger primes.
- pos -- if True then integers in the range [0,... p-1] will be used; if False integers in the range [(1-p)/2,..., p/2] will be used.
- ram_name -- The string used to represent the uniformizer.
- unram_name -- The string used to represent the trivial lift of a generator of the residue field over the prime field.
- var_name -- The string used to represent the user-specified generator of this extension ring or field.
- max_ram_terms -- Controls the maximum number of terms shown when printing in 'series', 'digits' or 'bars' mode.
- max_unram_terms -- For rings with non-prime residue fields, controls how many terms appear in the coefficient of each pi^n when printing in 'series' or 'bar' modes.
- max_terse_terms -- Controls the number of terms appearing when printing polynomial representations in 'terse' or 'val-unit' modes.
- sep -- Controls the separator used in 'bars' mode.
- alphabet -- Controls the alphabet used to translate p-adic digits into strings (so that no separator need be used in 'digits' mode).
- show_prec -- Determines whether `` + O(p^prec)`` is added. If None, uses defaults determined by print mode.
TESTS::
sage: R = Qp(7, print_mode='bars', print_sep='&') #indirect doctest """ global _printer_defaults else: # note that self.pos is reset to True if mode == 'digits' else: raise ValueError("digits printing mode only usable for totally ramified extensions with p at most the length of the alphabet (default 62). Try using print_mode = 'bars' instead.") else: else: raise ValueError("printing mode must be one of 'val-unit', 'series', 'terse', 'digits' or 'bars'") else: else: else: else: raise ValueError("max_ram_terms must be positive and fit in a long") else: raise ValueError("max_unram_terms must be positive and fit in a long") else: raise ValueError("max_terse_terms must be positive and fit in a long") else:
def __reduce__(self): """ Pickling.
TESTS::
sage: R = Zp(5, print_mode='bars', print_sep='&'); P = loads(dumps(R._printer)) sage: R._printer == P True sage: P._sep() '&' """
def __richcmp__(self, other, op): """ Comparison.
TESTS::
sage: R = Zp(5) sage: S = Zp(5,print_mode='bars') sage: R._printer == S._printer False """ return NotImplemented
def richcmp_modes(pAdicPrinter_class self, pAdicPrinter_class other, int op): """ Return a comparison of the printing modes of self and other.
Return 0 if and only if all relevant modes are equal (max_unram_terms is irrelevant if the ring is totally ramified over the base for example). This does not check if the rings are equal (to prevent infinite recursion in the comparison functions of p-adic rings), but it does check if the primes are the same (since the prime affects whether pos is relevant).
EXAMPLES::
sage: R = Qp(7, print_mode='digits', print_pos=True) sage: S = Qp(7, print_mode='digits', print_pos=False) sage: R._printer == S._printer True sage: R = Qp(7) sage: S = Qp(7,print_mode='val-unit') sage: R == S False sage: R._printer < S._printer True """
return richcmp_not_equal(p, q, op)
return richcmp_not_equal(lx, rx, op)
return richcmp_not_equal(lx, rx, op)
return richcmp_not_equal(lx, rx, op)
return richcmp_not_equal(lx, rx, op)
return richcmp_not_equal(lx, rx, op)
return richcmp_not_equal(lx, rx, op)
lx = self.var_name rx = other.var_name if lx != rx: return richcmp_not_equal(lx, rx, op) lx = self.max_terse_terms rx = other.max_terse_terms if lx != rx: return richcmp_not_equal(lx, rx, op) lx = self.show_prec rx = other.show_prec if lx != rx: return richcmp_not_equal(lx, rx, op)
def _repr_(self): """ Representation of this printer.
EXAMPLES::
sage: Zp(5)._printer #indirect doctest series printer for 5-adic Ring with capped relative precision 20 """
def __enter__(self): """ Used for context printing.
EXAMPLES::
sage: from sage.rings.padics.padic_printing import pAdicPrinter sage: R = Zp(5,5); a = R(-1); a 4 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + O(5^5) sage: with pAdicPrinter(R, {'pos': False}): a -1 + O(5^5) """
def dict(self): """ Returns a dictionary storing all of self's printing options.
EXAMPLES::
sage: D = Zp(5)._printer.dict(); D['sep'] '|' """
def __exit__(self, type, value, traceback): """ Used for context printing.
EXAMPLES::
sage: from sage.rings.padics.padic_printing import pAdicPrinter sage: R = Zp(5,5); a = R(-1); a 4 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + O(5^5) sage: with pAdicPrinter(R, {'pos': False}): a -1 + O(5^5) sage: a 4 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + O(5^5) """
def _pos(self): """ Accesses self.pos.
EXAMPLES::
sage: R = Zp(5); R._printer._pos() True """
def _sep(self): """ Accesses self.sep.
EXAMPLES::
sage: R = Zp(5); R._printer._sep() '|' """
def _alphabet(self): """ Accesses self.pos.
EXAMPLES::
sage: R = Zp(17, print_mode='digits'); R._printer._alphabet() ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G') """
def _max_ram_terms(self): """ Accesses self.max_ram_terms.
EXAMPLES::
sage: R = Zp(5); R._printer._max_ram_terms() -1 """
def _max_unram_terms(self): """ Accesses self.max_unram_terms.
EXAMPLES::
sage: R = Zp(5); R._printer._max_unram_terms() -1 """
def _max_terse_terms(self): """ Accesses self.max_terse_terms.
EXAMPLES::
sage: R = Zp(5); R._printer._max_terse_terms() -1 """
def _show_prec(self): """ Accesses self.show_prec.
EXAMPLES::
sage: R = ZpFP(5); R._printer._show_prec() False """
def _ring(self): """ Accesses self.ring.
EXAMPLES::
sage: R = Zp(5,5); R._printer._ring() 5-adic Ring with capped relative precision 5 """
def _uniformizer_name(self): """ Accesses self.ram_name.
EXAMPLES::
sage: R = Zp(5,5); R._printer._uniformizer_name() '5' """
def _ram_name(self): """ Accesses self.ram_name.
EXAMPLES::
sage: R = Zp(5,5); R._printer._ram_name() '5' """
def _print_mode(self): """ Accesses self.mode.
EXAMPLES::
sage: R = Zp(5); R._printer._print_mode() 'series' """ elif self.mode == series: elif self.mode == terse: elif self.mode == digits: elif self.mode == bars:
def _base_p_list(self, value, pos): """ Returns a list of integers forming the base p expansion of value.
If pos is True, these integers will be in the range [0,..., p-1]; if pos is False, they will be in the range [(1-p)/2,..., p/2].
The first entry will be the coefficient of p^0, etc.
EXAMPLES::
sage: P = Zp(17)._printer sage: P._base_p_list(1298734,True) [2, 15, 5, 9, 15] sage: P._base_p_list(1298734,False) [2, -2, 6, -8, -1, 1] sage: P._base_p_list(Zp(17)(1298734),True) [2, 15, 5, 9, 15] sage: P._base_p_list(Zp(17)(1298734),False) [2, -2, 6, -8, -1, 1] """
cdef base_p_list(self, value, bint pos): """ Returns a list of integers forming the base p expansion of value.
If pos is True, these integers will be in the range [0,... p-1]; if po is False, they will be in the range [(1-p)/2,..., p/2].
The first entry will be the coefficient of p^0, etc.
EXAMPLES::
sage: P = Zp(17)._printer sage: P._base_p_list(1298734,True) #indirect doctest [2, 15, 5, 9, 15] sage: P._base_p_list(1298734,False) [2, -2, 6, -8, -1, 1] """ else:
def repr_gen(self, elt, do_latex, pos = None, mode = None, ram_name = None): """ The entry point for printing an element.
INPUT:
- elt -- a p-adic element of the appropriate ring to print.
- do_latex -- whether to return a latex representation or a normal one.
EXAMPLES::
sage: R = Zp(5,5); P = R._printer; a = R(-5); a 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + 4*5^5 + O(5^6) sage: P.repr_gen(a, False, pos=False) '-5 + O(5^6)' sage: P.repr_gen(a, False, ram_name='p') '4*p + 4*p^2 + 4*p^3 + 4*p^4 + 4*p^5 + O(p^6)' """ cdef int _mode cdef bint _pos _mode = val_unit elif mode == 'digits': _mode = digits elif mode == 'bars': _mode = bars else: raise ValueError("printing mode must be one of 'val-unit', 'series', 'terse', 'bars', or 'digits'") else: else:
cdef _repr_gen(self, pAdicGenericElement elt, bint do_latex, bint pos, int mode, ram_name): r""" Prints a string representation of the element. See __init__ for more details on print modes.
EXAMPLES::
sage: R = Zp(7,4,'capped-rel','val-unit'); a = R(364); a #indirect doctest 7 * 52 + O(7^5) sage: print(a.str('terse')) 364 + O(7^5) sage: print(a.str('series')) 3*7 + 7^3 + O(7^5) sage: K = Qp(7,4,'capped-rel','val-unit'); a = K(364); a 7 * 52 + O(7^5) sage: print(a.str('series')) 3*7 + 7^3 + O(7^5) sage: padic_printing.sep('') sage: K = Qp(7, print_mode='digits') sage: repr(K(1/2)) '...33333333333333333334' sage: repr(K(1/42)) '...5555555555555555555.6' sage: padic_printing.sep('|') sage: repr(Qp(97, print_mode='bars')(1/13)) '...29|82|7|44|74|59|67|14|89|52|22|37|29|82|7|44|74|59|67|15' """ cdef Py_ssize_t i elif mode == terse: elif mode == digits: prec = elt.precision_absolute() if prec > 0: s = "..." + (self.alphabet[0] * prec) else: s = "...?." + ("?" * (-prec)) + self.alphabet[0] elif mode == bars: if self.base or self._ring().f() == 1: zero = '0' else: zero = '[]' prec = elt.precision_absolute() if prec > 0: L = [zero] * prec s = "..." + self.sep.join(L) else: L = ['.'] + (['?'] * (-prec)) + [zero] s = "..." + self.sep + self.sep.join(L) if elt.valuation() == 0: s = self._repr_spec(elt, do_latex, pos, terse, 0, ram_name) elif elt.valuation() == 1: s = "%s \\cdot %s"%(ram_name, self._repr_spec(elt.unit_part(), do_latex, pos, terse, 1, ram_name)) else: s = "%s^{%s} \\cdot %s"%(ram_name, elt.valuation(), self._repr_spec(elt.unit_part(), do_latex, pos, terse, 1, ram_name)) else: else: else: else: # The following step should work since mode is only allowed to be digits in the case of totally ramified extensions # with primes smaller than the length of the alphabet else: else: else: else: else: else: else: else: else: # mode == terse or series else: else:
cdef _repr_spec(self, pAdicGenericElement elt, bint do_latex, bint pos, int mode, bint paren, ram_name): """ A function used by repr_gen for terse and series printing.
Should not be called if elt is an exact or inexact zero. """ cdef Integer lift_z, pprec cdef int ZZ_pEX cdef Py_ssize_t i, j cdef long val #cdef bint ellipsis = 0 cdef ellipsis_unram cdef bint integral else: else: else: # if v>=0, _terse_frac only uses the first input. # if v<0, _terse_frac doesn't use the first input at all, and expects the unit part in the third input. else: else: # mode == series else: else: # not self.base else: else: L = [a.split() for a in L] L = [[("" if b == "0" else b) for b in a] for a in L] L, ellipsis = self._truncate_list(L, self.max_ram_terms, "") raise NotImplementedError else: else: else: else: s += " - " s += self._var(self.var_name, i, do_latex) else: else: else: else: # series # since elt was not supposed to be zero, this should give a non-empty list. raise RuntimeError("repr_spec called on zero") raise RuntimeError("need to have specified a name for the unramified variable") else: else: # since len(term) > 0, len(L[i]) == 1. else: else: else: else: # f = 1, so no unramified printing required s += self._plus_ellipsis(do_latex)
cdef _var(self, x, exp, do_latex): """ Returns a representation of 'x^exp', latexed if necessary. """ return "%s^{%s}"%(x, exp) else:
cdef _dot_var(self, x, exp, do_latex): """ Returns a representation of '*x^exp', latexed if necessary. """ return " \\cdot %s"%(x) else: return " \\cdot %s^{%s}"%(x, exp) else:
cdef _co_dot_var(self, co, x, exp, do_latex): """ Returns a representation of 'co*x^exp', latexed if necessary.
co should be greater than 0 """ else: return "%s^{%s}"%(x, exp) else: return "%s \\cdot %s^{%s}"%(co, x, exp) else:
cdef _plus_ellipsis(self, bint do_latex): """ Returns a representation of '+ ...', latexed if necessary. """ return " + \\cdots" else:
cdef _ellipsis(self, bint do_latex): """ Returns a representation of '...', latexed if necessary. """ return "\\cdots" else:
cdef _truncate_list(self, L, max_terms, zero): """ Takes a list L of coefficients and returns a list with at most max_terms nonzero terms.
INPUT:
- L -- a list
- max_terms -- nonnegative integer (or -1, in which case no truncation occurs)
- zero -- what should be considered zero, usually 0 or [].
OUTPUT:
- Truncated list -- later terms are removed. - Boolean -- whether any truncation occurred. """ cdef Py_ssize_t i, nonzero_index
cdef _print_unram_term(self, L, bint do_latex, polyname, long max_unram_terms, long expshift, bint increasing): """ Returns a string representation of L when considered as a polynomial, truncating to at most max_unram_terms nonzero terms.
INPUT:
- L -- A list of coefficients.
- do_latex -- whether to print latex-style.
- polyname -- the name for the variable.
- max_unram_terms -- a maximum number of terms before truncation occurs and an ellipsis is added. -1 indicates no truncation should happen.
- expshift -- a shift for all the exponents of the variable.
- increasing -- Whether to order the exponents in increasing fashion. """ cdef Py_ssize_t j, newj for j from 0 <= j < len(L): exp = j + expshift if L[j] != 0: if max_unram_terms == 0: return "(" + self._ellipsis(do_latex) + ")" elif max_unram_terms == 1: s = self._co_dot_var(L[j], polyname, exp, do_latex) s += self._plus_ellipsis(do_latex) return s else: count += 1 if count == max_unram_terms: #this will never trigger if max_unram_terms == -1 newj = len(L) - 1 while L[newj] == 0: newj -= 1 if newj != j: exp = newj + expshift s += self._plus_ellipsis(do_latex) s = self._print_term_of_poly(s, L[newj], do_latex, polyname, exp) return s else: s = self._print_term_of_poly(s, L[j], do_latex, polyname, exp) else: elif max_unram_terms == 1: else: else:
cdef _terse_frac(self, a, v, u, ram_name, bint do_latex): """ Returns a representation of a=u/ram_name^v, latexed if necessary. """ if v >= 0: arep = a._latex_() elif v == -1: arep = "\\frac{%s}{%s}"%(u, ram_name) else: arep = "\\frac{%s}{%s^{%s}}"%(u, ram_name, -v) else: else:
cdef _print_list_as_poly(self, L, bint do_latex, polyname, long expshift, bint increasing): """ Prints a list L as a polynomial.
INPUT:
- L -- A list of coefficients.
- do_latex -- whether to print latex-style.
- polyname -- the name for the variable.
- expshift -- a shift for all the exponents of the variable.
- increasing -- Whether to order the exponents in increasing fashion. """ cdef Py_ssize_t j cdef long exp else: for j from len(L) - 1 >= j >= 0: exp = j + expshift s = self._print_term_of_poly(s, L[j], do_latex, polyname, exp)
cdef _print_term_of_poly(self, s, coeff, bint do_latex, polyname, long exp): """ Appends +coeff*polyname^exp to s, latexed if necessary. """ else: |