# HG changeset patch
# User David Loeffler <d.loeffler.01@cantab.net>
# Date 1295440053 0
# Node ID 9eee036bab660d5de5b282204eff3eceb474b0db
# Parent  f030fa5f7635260da38eebd1cf44ea31f287929c
#10658: local components of modular forms

diff --git a/doc/en/reference/modmisc.rst b/doc/en/reference/modmisc.rst
--- a/doc/en/reference/modmisc.rst
+++ b/doc/en/reference/modmisc.rst
@@ -11,6 +11,11 @@
    sage/modular/dims
    sage/modular/buzzard
 
+   sage/modular/local_comp/local_comp
+   sage/modular/local_comp/smoothchar
+   sage/modular/local_comp/type_space
+   sage/modular/local_comp/liftings
+
    sage/modular/etaproducts
    sage/modular/overconvergent/weightspace
    sage/modular/overconvergent/genus0
diff --git a/sage/modular/all.py b/sage/modular/all.py
--- a/sage/modular/all.py
+++ b/sage/modular/all.py
@@ -28,5 +28,7 @@
 
 from overconvergent.all import *
 
+from local_comp.all import *
+
 from cusps_nf import NFCusp, NFCusps, NFCusps_clear_cache, Gamma0_NFCusps
 
diff --git a/sage/modular/local_comp/__init__.py b/sage/modular/local_comp/__init__.py
new file mode 100644
diff --git a/sage/modular/local_comp/all.py b/sage/modular/local_comp/all.py
new file mode 100644
--- /dev/null
+++ b/sage/modular/local_comp/all.py
@@ -0,0 +1,1 @@
+from local_comp import LocalComponent
diff --git a/sage/modular/local_comp/liftings.py b/sage/modular/local_comp/liftings.py
new file mode 100644
--- /dev/null
+++ b/sage/modular/local_comp/liftings.py
@@ -0,0 +1,164 @@
+r"""
+Helper functions for local components
+
+This module contains various functions relating to lifting elements of
+`\mathrm{SL}_2(\ZZ / N\ZZ)` to `\mathrm{SL}_2(\ZZ)`, and other related
+problems. 
+"""
+
+from sage.rings.all import ZZ, Zmod
+from sage.rings.arith import crt, inverse_mod
+from sage.modular.modsym.p1list import lift_to_sl2z
+
+def lift_to_gamma1(g, m, n):
+    r"""
+    If ``g = [a,b,c,d]`` is a list of integers defining a `2 \times 2` matrix
+    whose determinant is `1 \pmod m`, return a list of integers giving the
+    entries of a matrix which is congruent to `g \pmod m` and to
+    `\begin{pmatrix} 1 & * \\ 0 & 1 \end{pmatrix} \pmod n`. Here `m` and `n`
+    must be coprime.
+
+    Here `m` and `n` should be coprime positive integers. Either of `m` and `n`
+    can be `1`. If `n = 1`, this still makes perfect sense; this is what is
+    called by the function :func:`~lift_matrix_to_sl2z`. If `m = 1` this is a
+    rather silly question, so we adopt the convention of always returning the
+    identity matrix.
+
+    The result is always a list of Sage integers (unlike ``lift_to_sl2z``,
+    which tends to return Python ints).
+
+    EXAMPLE::
+
+        sage: from sage.modular.local_comp.liftings import lift_to_gamma1
+        sage: A = matrix(ZZ, 2, lift_to_gamma1([10, 11, 3, 11], 19, 5)); A
+        [371  68]
+        [ 60  11]
+        sage: A.det() == 1
+        True
+        sage: A.change_ring(Zmod(19))
+        [10 11]
+        [ 3 11]
+        sage: A.change_ring(Zmod(5))
+        [1 3]
+        [0 1]
+        sage: m = list(SL2Z.random_element())
+        sage: n = lift_to_gamma1(m, 11, 17)
+        sage: assert matrix(Zmod(11), 2, n) == matrix(Zmod(11),2,m)
+        sage: assert matrix(Zmod(17), 2, [n[0], 0, n[2], n[3]]) == 1
+        sage: type(lift_to_gamma1([10,11,3,11],19,5)[0])
+        <type 'sage.rings.integer.Integer'>
+
+    Tests with `m = 1` and with `n = 1`::
+
+        sage: lift_to_gamma1([1,1,0,1], 5, 1)
+        [1, 1, 0, 1]
+        sage: lift_to_gamma1([2,3,11,22], 1, 5)
+        [1, 0, 0, 1]
+    """
+    if m == 1: 
+        return [ZZ(1),ZZ(0),ZZ(0),ZZ(1)]
+    a,b,c,d = [ZZ(x) for x in g]
+    if not (a*d - b*c) % m == 1:
+        raise ValueError( "Determinant is {0} mod {1}, should be 1".format((a*d - b*c) % m, m) )
+    c2 = crt(c, 0, m, n)
+    d2 = crt(d, 1, m, n)
+    a3,b3,c3,d3 = map(ZZ, lift_to_sl2z(c2,d2,m*n))
+    r = (a3*b - b3*a) % m
+    return [a3 + r*c3, b3 + r*d3, c3, d3]
+
+def lift_matrix_to_sl2z(A, N):
+    r"""
+    Given a list of length 4 representing a 2x2 matrix over `\ZZ / N\ZZ` with
+    determinant 1 (mod `N`), lift it to a 2x2 matrix over `\ZZ` with
+    determinant 1.
+
+    This is a special case of :func:`~lift_to_gamma1`, and is coded as such.
+
+    TESTS::
+
+        sage: from sage.modular.local_comp.liftings import lift_matrix_to_sl2z
+        sage: lift_matrix_to_sl2z([10, 11, 3, 11], 19)
+        [29, 106, 3, 11]
+        sage: type(_[0])
+        <type 'sage.rings.integer.Integer'>
+        sage: lift_matrix_to_sl2z([2,0,0,1], 5)
+        Traceback (most recent call last):
+        ...
+        ValueError: Determinant is 2 mod 5, should be 1
+    """
+    return lift_to_gamma1(A, N, 1)
+
+def lift_gen_to_gamma1(m, n):
+    r"""
+    Return four integers defining a matrix in `\mathrm{SL}_2(\ZZ)` which is
+    congruent to `\begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix} \pmod m` and
+    lies in the subgroup `\begin{pmatrix} 1 & * \\ 0 & 1 \end{pmatrix} \pmod
+    n`.
+    
+    This is a special case of :func:`~lift_to_gamma1`, and is coded as such.
+
+    EXAMPLE::
+
+        sage: from sage.modular.local_comp.liftings import lift_gen_to_gamma1
+        sage: A = matrix(ZZ, 2, lift_gen_to_gamma1(9, 8)); A
+        [441  62]
+        [ 64   9]
+        sage: A.change_ring(Zmod(9))
+        [0 8]
+        [1 0]
+        sage: A.change_ring(Zmod(8))
+        [1 6]
+        [0 1]
+        sage: type(lift_gen_to_gamma1(9, 8)[0])
+        <type 'sage.rings.integer.Integer'>
+    """
+    return lift_to_gamma1([0,-1,1,0], m, n)
+
+def lift_uniformiser_odd(p, u, n):
+    r"""
+    Construct a matrix over `\ZZ` whose determinant is `p`, and which is
+    congruent to `\begin{pmatrix} 0 & -1 \\ p & 0 \end{pmatrix} \pmod{p^u}` and
+    to `\begin{pmatrix} p & 0 \\ 0 & 1\end{pmatrix} \pmod n`.
+
+    This is required for the local components machinery in the "ramified" case
+    (when the exponent of `p` dividing the level is odd).
+
+    EXAMPLE::
+
+        sage: from sage.modular.local_comp.liftings import lift_uniformiser_odd
+        sage: lift_uniformiser_odd(3, 2, 11)
+        [432, 377, 165, 144]
+        sage: type(lift_uniformiser_odd(3, 2, 11)[0])
+        <type 'sage.rings.integer.Integer'>
+    """
+    g = lift_gen_to_gamma1(p**u, n)
+    return [p*g[0], g[1], p*g[2], g[3]]
+
+    
+def lift_ramified(g, p, u, n):
+    r"""
+    Given four integers `a,b,c,d` with `p \mid c` and `ad - bc = 1 \pmod{p^u}`,
+    find `a',b',c',d'` congruent to `a,b,c,d \pmod{p^u}`, with `c' = c
+    \pmod{p^{u+1}}`, such that `a'd' - b'c'` is exactly 1, and `\begin{pmatrix}
+    a & b \\ c & d \end{pmatrix}` is in `\Gamma_1(n)`.
+
+    Algorithm: Uses :func:`~lift_to_gamma1` to get a lifting modulo `p^u`, and
+    then adds an appropriate multiple of the top row to the bottom row in order
+    to get the bottom-left entry correct modulo `p^{u+1}`.
+
+    EXAMPLE::
+
+        sage: from sage.modular.local_comp.liftings import lift_ramified
+        sage: lift_ramified([2,2,3,2], 3, 1, 1)
+        [5, 8, 3, 5]
+        sage: lift_ramified([8,2,12,2], 3, 2, 23)
+        [323, 110, -133584, -45493]
+        sage: type(lift_ramified([8,2,12,2], 3, 2, 23)[0])
+        <type 'sage.rings.integer.Integer'>
+    """
+    a,b,c,d = lift_to_gamma1(g, p**u, n)
+    r = crt( (c - g[2]) / p**u * inverse_mod(a, p), 0, p, n)
+    c = c - p**u * r * a
+    d = d - p**u * r * b
+    # assert (c - g[2]) % p**(u+1) == 0
+    return [a,b,c,d]
diff --git a/sage/modular/local_comp/local_comp.py b/sage/modular/local_comp/local_comp.py
new file mode 100644
--- /dev/null
+++ b/sage/modular/local_comp/local_comp.py
@@ -0,0 +1,736 @@
+r"""
+Local components of modular forms
+
+If `f` is a (new, cuspidal, normalised) modular eigenform, then one can
+associate to `f` an *automorphic representation* `\pi_f` of the group
+`\operatorname{GL}_2(\mathbf{A})` (where `\mathbf{A}` is the adele ring of
+`\QQ`). This object factors as a restricted tensor product of components
+`\pi_{f, v}` for each place of `\QQ`. These are infinite-dimensional
+representations, but they are specified by a finite amount of data, and this
+module provides functions which determine a description of the local factor
+`\pi_{f, p}` at a finite prime `p`.
+
+The functions in this module are based on the algorithms described in:
+
+.. [LW11] David Loeffler and Jared Weinstein, *On the computation of local components of a newform*, 
+   Mathematics of Computation (to appear), 2011. `Online version
+   <http://dx.doi.org/10.1090/S0025-5718-2011-02530-5>`_.
+
+AUTHORS:
+
+- David Loeffler
+- Jared Weinstein
+"""
+
+import operator
+from sage.structure.sage_object     import SageObject
+from sage.rings.all                 import QQ, ZZ, Zmod, QQbar, PolynomialRing, polygen
+from sage.modular.modform.element   import Newform
+from sage.modular.dirichlet         import DirichletGroup
+from sage.misc.cachefunc            import cached_method
+from sage.misc.abstract_method      import abstract_method
+from sage.structure.sequence        import Sequence
+
+from type_space                     import TypeSpace
+from smoothchar                     import SmoothCharacterGroupQp, SmoothCharacterGroupUnramifiedQuadratic, SmoothCharacterGroupRamifiedQuadratic
+
+def LocalComponent(f, p, twist_factor=None):
+    r"""
+    Calculate the local component at the prime `p` of the automorphic
+    representation attached to the newform `f`.
+    
+    INPUT:
+    
+    - ``f`` (:class:`~sage.modular.modform.element.Newform`) a newform of weight `k \ge 2`
+    - ``p`` (integer) a prime
+    - ``twist_factor`` (integer) an integer congruent to `k` modulo 2 (default: `k - 2`)
+    
+    .. note::
+    
+        The argument ``twist_factor`` determines the choice of normalisation: if it is 
+        set to `j \in \ZZ`, then the central character of `\pi_{f, \ell}` maps `\ell` 
+        to `\ell^j \varepsilon(\ell)` for almost all `\ell`, where `\varepsilon` is the 
+        Nebentypus character of `f`.
+        
+        In the analytic theory it is conventional to take `j = 0` (the "Langlands 
+        normalisation"), so the representation `\pi_f` is unitary; however, this is 
+        inconvenient for `k` odd, since in this case one needs to choose a square root of `p` 
+        and thus the map `f \to \pi_{f}` is not Galois-equivariant. Hence we use, by default, the 
+        "Hecke normalisation" given by `j = k - 2`. This is also the most natural normalisation 
+        from the perspective of modular symbols.
+
+        We also adopt a slightly unusual definition of the principal series: we
+        define `\pi(\chi_1, \chi_2)` to be the induction from the Borel subgroup of
+        the character of the maximal torus `\begin{pmatrix} x & \\ & y
+        \end{pmatrix} \mapsto \chi_1(a) \chi_2(b) |b|`, so its central character is
+        `z \mapsto \chi_1(z) \chi_2(z) |z|`. Thus `\chi_1 \chi_2` is the
+        restriction to `\QQ_p^\times` of the unique character of the id\'ele class
+        group mapping `\ell` to `\ell^{k-1} \varepsilon(\ell)` for almost all `\ell`. 
+        This has the property that the *set* `\{\chi_1, \chi_2\}` also depends 
+        Galois-equivariantly on `f`.
+        
+    EXAMPLE::
+    
+        sage: Pi = LocalComponent(Newform('49a'), 7); Pi
+        Smooth representation of GL_2(Q_7) with conductor 7^2
+        sage: Pi.central_character()
+        Character of Q_7*, of level 0, mapping 7 |--> 1
+        sage: Pi.species()
+        'Supercuspidal'
+        sage: Pi.characters()
+        [
+        Character of unramified extension Q_7(s)* (s^2 + 6*s + 3 = 0), of level 1, mapping s |--> d, 7 |--> 1,
+        Character of unramified extension Q_7(s)* (s^2 + 6*s + 3 = 0), of level 1, mapping s |--> -d, 7 |--> 1
+        ]
+    """
+    p = ZZ(p)
+    if not p.is_prime():
+        raise ValueError( "p must be prime" )
+    if not isinstance(f, Newform):
+        raise TypeError( "f (=%s of type %s) should be a Newform object" % (f, type(f)) )
+        
+    r = f.level().valuation(p)
+    if twist_factor is None:
+        twist_factor = ZZ(f.weight() - 2)
+    else:
+        twist_factor = ZZ(twist_factor)
+    if r == 0: 
+        return UnramifiedPrincipalSeries(f, p, twist_factor)
+    c = ZZ(f.character().conductor()).valuation(p)
+    if f[p] != 0:
+        if c == r:
+            return PrimitivePrincipalSeries(f, p, twist_factor)
+        if c == 0 and r == 1:
+            return PrimitiveSpecial(f, p, twist_factor)
+    Xf = TypeSpace(f, p)
+    if Xf.is_minimal():
+        return PrimitiveSupercuspidal(f, p, twist_factor)
+    else:
+        raise NotImplementedError( "Form %s is not %s-primitive" % (f, p) )
+
+class LocalComponentBase(SageObject):
+    r"""
+    Base class for local components of newforms. Not to be directly instantiated; use the :func:`~LocalComponent` constructor function.
+    """
+    
+    def __init__(self, newform, prime, twist_factor):
+        r"""
+        Standard initialisation function.
+        
+        EXAMPLE::
+        
+            sage: LocalComponent(Newform('49a'), 7) # indirect doctest
+            Smooth representation of GL_2(Q_7) with conductor 7^2
+        """
+        self._p = prime
+        self._f = newform
+        self._twist_factor = twist_factor
+
+    @abstract_method
+    def species(self):
+        r"""
+        The species of this local component, which is either 'Principal
+        Series', 'Special' or 'Supercuspidal'.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.local_comp import LocalComponentBase
+            sage: LocalComponentBase(Newform('50a'), 3, 0).species()
+            Traceback (most recent call last):
+            ...
+            NotImplementedError: <abstract method species at ...>
+        """
+        pass
+
+    @abstract_method
+    def check_tempered(self):
+        r"""
+        Check that this representation is quasi-tempered, i.e. `\pi \otimes
+        |\det|^{j/2}` is tempered. It is well known that local components of
+        modular forms are *always* tempered, so this serves as a useful check
+        on our computations.
+
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.local_comp import LocalComponentBase
+            sage: LocalComponentBase(Newform('50a'), 3, 0).check_tempered()
+            Traceback (most recent call last):
+            ...
+            NotImplementedError: <abstract method check_tempered at ...>
+        """
+        pass
+
+    def _repr_(self):
+        r"""
+        String representation of self.
+        
+        EXAMPLE::
+        
+            sage: LocalComponent(Newform('50a'), 5)._repr_()
+            'Smooth representation of GL_2(Q_5) with conductor 5^2'
+        """
+        return "Smooth representation of GL_2(Q_%s) with conductor %s^%s" % (self.prime(), self.prime(), self.conductor())
+
+    def newform(self):
+        r"""
+        The newform of which this is a local component.
+        
+        EXAMPLE::
+        
+            sage: LocalComponent(Newform('50a'), 5).newform()
+            q - q^2 + q^3 + q^4 + O(q^6)
+        """
+        return self._f
+
+    def prime(self):
+        r"""
+        The prime at which this is a local component.
+        
+        EXAMPLE::
+        
+            sage: LocalComponent(Newform('50a'), 5).prime()
+            5
+        """
+        return self._p
+
+    def conductor(self):
+        r"""
+        The smallest `r` such that this representation has a nonzero vector fixed by the subgroup
+        `\begin{pmatrix} * & * \\ 0 & 1\end{pmatrix} \pmod{p^r}`. This is equal to the power of `p` dividing the level of the corresponding newform.
+        
+        EXAMPLE::
+        
+            sage: LocalComponent(Newform('50a'), 5).conductor()
+            2
+        """
+        return self.newform().level().valuation(self.prime())
+        
+    def coefficient_field(self):
+        r"""
+        The field `K` over which this representation is defined. This is the field generated by the Hecke eigenvalues of the corresponding newform (over whatever base ring the newform is created).
+        
+        EXAMPLE::
+        
+            sage: LocalComponent(Newforms(50)[0], 3).coefficient_field()
+            Rational Field
+            sage: LocalComponent(Newforms(Gamma1(10), 3, base_ring=QQbar)[0], 5).coefficient_field()
+            Algebraic Field
+            sage: LocalComponent(Newforms(DirichletGroup(5).0, 7,names='c')[0], 5).coefficient_field()
+            Number Field in c0 with defining polynomial x^2 + (5*zeta4 + 5)*x - 88*zeta4 over its base field
+        """
+        return self.newform().hecke_eigenvalue_field()
+
+    def twist_factor(self):
+        r"""
+        The unique `j` such that `\begin{pmatrix} p & 0 \\ 0 & p\end{pmatrix}` 
+        acts as multiplication by `p^j` times a root of unity.
+
+        There are various conventions for this; see the documentation of the
+        :func:`~LocalComponent` constructor function for more information.
+        
+        The twist factor should have the same parity as the weight of the form,
+        since otherwise the map sending `f` to its local component won't be
+        Galois equivariant.
+        
+        EXAMPLE::
+        
+            sage: LocalComponent(Newforms(50)[0], 3).twist_factor()
+            0
+            sage: LocalComponent(Newforms(50)[0], 3, twist_factor=173).twist_factor()
+            173
+        """
+        return self._twist_factor
+
+    def central_character(self):
+        r"""
+        Return the central character of this representation. This is the
+        restriction to `\QQ_p^\times` of the unique smooth character `\omega`
+        of `\mathbf{A}^\times / \QQ^\times` such that `\omega(\varpi_\ell) =
+        \ell^j \varepsilon(\ell)` for all primes `\ell \nmid Np`, where
+        `\varpi_\ell` is a uniformiser at `\ell`, `\varepsilon` is the
+        Nebentypus character of the newform `f`, and `j` is the twist factor
+        (see the documentation for :func:`~LocalComponent`).
+        
+        EXAMPLES::
+        
+            sage: LocalComponent(Newform('27a'), 3).central_character()
+            Character of Q_3*, of level 0, mapping 3 |--> 1
+            
+            sage: LocalComponent(Newforms(Gamma1(5), 5, names='c')[0], 5).central_character()
+            Character of Q_5*, of level 1, mapping 2 |--> c0 + 1, 5 |--> 125
+            
+            sage: LocalComponent(Newforms(DirichletGroup(24)([1, -1,-1]), 3, names='a')[0], 2).central_character()
+            Character of Q_2*, of level 3, mapping 7 |--> 1, 5 |--> -1, 2 |--> -2
+        """
+        from sage.rings.arith import crt
+        chi = self.newform().character()
+        f = self.prime() ** self.conductor()
+        N = self.newform().level() // f
+        G = DirichletGroup(f, self.coefficient_field())
+        chip = G([chi(crt(ZZ(x), 1, f, N)) for x in G.unit_gens()]).primitive_character()
+        a = crt(1, self.prime(), f, N)        
+        
+        if chip.conductor() == 1:
+            return SmoothCharacterGroupQp(self.prime(), self.coefficient_field()).character(0, [chi(a) * self.prime()**self.twist_factor()])
+        else:
+            return SmoothCharacterGroupQp(self.prime(), self.coefficient_field()).character(chip.conductor().valuation(self.prime()), list((~chip).values_on_gens()) + [chi(a) * self.prime()**self.twist_factor()])
+
+    def __cmp__(self, other):
+        r"""
+        Comparison function.
+
+        EXAMPLE::
+            
+            sage: Pi = LocalComponent(Newform("50a"), 5)
+            sage: Pi == LocalComponent(Newform("50a"), 3)
+            False
+            sage: Pi == LocalComponent(Newform("50b"), 5)
+            False
+            sage: Pi == QQ
+            False
+            sage: Pi == None
+            False
+            sage: Pi == loads(dumps(Pi))
+            True
+        """
+        return (cmp(type(self), type(other))
+            or cmp(self.prime(), other.prime())
+            or cmp(self.newform(), other.newform())
+            or cmp(self.twist_factor(), other.twist_factor()))
+
+class PrincipalSeries(LocalComponentBase):
+    r"""
+    A principal series representation. This is an abstract base class, not to
+    be instantiated directly; see the subclasses
+    :class:`~UnramifiedPrincipalSeries` and :class:`~PrimitivePrincipalSeries`.
+    """
+
+    def species(self):
+        r"""
+        The species of this local component, which is either 'Principal
+        Series', 'Special' or 'Supercuspidal'.
+        
+        EXAMPLE::
+        
+            sage: LocalComponent(Newform('50a'), 3).species()
+            'Principal Series'
+        """
+        return "Principal Series"
+
+    def check_tempered(self):
+        r"""
+        Check that this representation is tempered (after twisting by
+        `|\det|^{j/2}`), i.e. that `|\chi_1(p)| = |\chi_2(p)| = p^{(j + 1)/2}`.
+        This follows from the Ramanujan--Petersson conjecture, as proved by
+        Deligne.
+
+        EXAMPLE::
+        
+            sage: LocalComponent(Newform('49a'), 3).check_tempered()
+        """
+        c1, c2 = self.characters()
+        K = c1.base_ring()
+        p = self.prime()
+        w = QQbar(p)**((1 + self.twist_factor()) / 2)
+        for sigma in K.embeddings(QQbar):
+            assert sigma(c1(p)).abs() == sigma(c2(p)).abs() == w
+
+    @abstract_method
+    def characters(self):
+        r"""
+        Return the two characters `(\chi_1, \chi_2)` such this representation
+        `\pi_{f, p}` is equal to the principal series `\pi(\chi_1, \chi_2)`. 
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.local_comp import PrincipalSeries
+            sage: PrincipalSeries(Newform('50a'), 3, 0).characters()
+            Traceback (most recent call last):
+            ...
+            NotImplementedError: <abstract method characters at ...>
+        """
+        pass
+
+class UnramifiedPrincipalSeries(PrincipalSeries):
+    r"""
+    An unramified principal series representation of `{\rm GL}_2(\QQ_p)`
+    (corresponding to a form whose level is not divisible by `p`).
+    
+    EXAMPLE::
+    
+        sage: Pi = LocalComponent(Newform('50a'), 3)
+        sage: Pi.conductor()
+        0
+        sage: type(Pi)
+        <class 'sage.modular.local_comp.local_comp.UnramifiedPrincipalSeries'>
+        sage: TestSuite(Pi).run()
+    """
+
+    def satake_polynomial(self):
+        r"""
+        Return the Satake polynomial of this representation, i.e.~the polynomial whose roots are `\chi_1(p), \chi_2(p)`
+        where this representation is `\pi(\chi_1, \chi_2)`. Concretely, this is the polynomial
+        
+        .. math::
+        
+            X^2 - p^{(j - k + 2)/2} a_p(f) X + p^{j + 1} \varepsilon(p)`.
+            
+        An error will be raised if `j \ne k \bmod 2`.
+        
+        EXAMPLES::
+        
+            sage: LocalComponent(Newform('11a'), 17).satake_polynomial()
+            X^2 + 2*X + 17
+            sage: LocalComponent(Newform('11a'), 17, twist_factor = -2).satake_polynomial()
+            X^2 + 2/17*X + 1/17
+        """
+        p = self.prime()
+        return PolynomialRing(self.coefficient_field(), 'X')([
+                self.central_character()(p)*p, 
+                -self.newform()[p] * p**((self.twist_factor() - self.newform().weight() + 2)/2), 
+                1
+              ])
+
+    def characters(self):
+        r"""
+        Return the two characters `(\chi_1, \chi_2)` such this representation
+        `\pi_{f, p}` is equal to the principal series `\pi(\chi_1, \chi_2)`. 
+        These are the unramified characters mapping `p` to the roots of the Satake polynomial, 
+        so in most cases (but not always) they will be defined over an
+        extension of the coefficient field of self.
+        
+        EXAMPLES::
+        
+            sage: LocalComponent(Newform('11a'), 17).characters()
+            [
+            Character of Q_17*, of level 0, mapping 17 |--> d,
+            Character of Q_17*, of level 0, mapping 17 |--> -d - 2
+            ]
+            sage: LocalComponent(Newforms(Gamma1(5), 6, names='a')[1], 3).characters()
+            [
+            Character of Q_3*, of level 0, mapping 3 |--> -3/2*a1 + 12,
+            Character of Q_3*, of level 0, mapping 3 |--> -3/2*a1 - 12
+            ]
+        """
+        f = self.satake_polynomial()
+        if not f.is_irreducible():
+            # This can happen; see the second example above
+            d = f.roots()[0][0]
+        else:
+            d = self.coefficient_field().extension(f, 'd').gen()
+        G = SmoothCharacterGroupQp(self.prime(), d.parent())
+        return Sequence([G.character(0, [d]), G.character(0, [self.newform()[self.prime()] - d])], cr=True, universe=G)
+    
+class PrimitivePrincipalSeries(PrincipalSeries):
+    r"""
+    A ramified principal series of the form `\pi(\chi_1, \chi_2)`
+    where `\chi_1` is unramified but `\chi_2` is not.
+    
+    EXAMPLE::
+    
+        sage: Pi = LocalComponent(Newforms(Gamma1(13), 2, names='a')[0], 13)
+        sage: type(Pi)
+        <class 'sage.modular.local_comp.local_comp.PrimitivePrincipalSeries'>
+        sage: TestSuite(Pi).run()
+    """
+    
+    def characters(self):
+        r"""
+        Return the two characters `(\chi_1, \chi_2)` such that the local component `\pi_{f, p}` is the induction of the character `\chi_1 \times \chi_2` of the Borel subgroup.
+        
+        EXAMPLE::
+            
+            sage: LocalComponent(Newforms(Gamma1(13), 2, names='a')[0], 13).characters()
+            [
+            Character of Q_13*, of level 0, mapping 13 |--> 3*a0 + 2,
+            Character of Q_13*, of level 1, mapping 2 |--> a0 + 2, 13 |--> -3*a0 - 7
+            ]
+        """
+        G = SmoothCharacterGroupQp(self.prime(), self.coefficient_field())
+        chi1 = G.character(0, [self.newform()[self.prime()]])
+        chi2 = G.character(0, [self.prime()]) * self.central_character() / chi1
+        return Sequence([chi1, chi2], cr=True, universe=G)
+
+class PrimitiveSpecial(LocalComponentBase):
+    r"""
+    A primitive special representation: that is, the Steinberg representation
+    twisted by an unramified character. All such representations have conductor
+    1.
+
+    EXAMPLES::
+
+        sage: Pi = LocalComponent(Newform('37a'), 37)
+        sage: Pi.species()
+        'Special'
+        sage: Pi.conductor()
+        1
+        sage: type(Pi)
+        <class 'sage.modular.local_comp.local_comp.PrimitiveSpecial'>
+        sage: TestSuite(Pi).run()
+    """
+
+    def species(self):
+        r"""
+        The species of this local component, which is either 'Principal
+        Series', 'Special' or 'Supercuspidal'.
+        
+        EXAMPLE::
+
+            sage: LocalComponent(Newform('37a'), 37).species()
+            'Special'
+        """
+        return "Special"
+        
+    def characters(self):
+        r"""
+        Return the defining characters of this representation. In this case, it
+        will return the unique unramified character `\chi` of `\QQ_p^\times`
+        such that this representation is equal to `\mathrm{St} \otimes \chi`,
+        where `\mathrm{St}` is the Steinberg representation (defined as the
+        quotient of the parabolic induction of the trivial character by its
+        trivial subrepresentation).
+
+        EXAMPLES:
+
+        Our first example is the newform corresponding to an elliptic curve of
+        conductor `37`. This is the nontrivial quadratic twist of Steinberg,
+        corresponding to the fact that the elliptic curve has non-split
+        multiplicative reduction at 37::
+
+            sage: LocalComponent(Newform('37a'), 37).characters()
+            [Character of Q_37*, of level 0, mapping 37 |--> -1]
+
+        We try an example in odd weight, where the central character isn't
+        trivial::
+
+            sage: Pi = LocalComponent(Newforms(DirichletGroup(21)([-1, 1]), 3, names='j')[0], 7); Pi.characters()
+            [Character of Q_7*, of level 0, mapping 7 |--> -1/2*j0^2 - 7/2]
+            sage: Pi.characters()[0] ^2 == Pi.central_character()
+            True
+
+        An example using a non-standard twist factor::
+
+            sage: Pi = LocalComponent(Newforms(DirichletGroup(21)([-1, 1]), 3, names='j')[0], 7, twist_factor=3); Pi.characters()
+            [Character of Q_7*, of level 0, mapping 7 |--> -7/2*j0^2 - 49/2]
+            sage: Pi.characters()[0]^2 == Pi.central_character()
+            True
+        """
+
+        return [SmoothCharacterGroupQp(self.prime(), self.coefficient_field()).character(0, [self.newform()[self.prime()] * self.prime() ** ((self.twist_factor() - self.newform().weight() + 2)/2)])]
+
+    def check_tempered(self):
+        r"""
+        Check that this representation is tempered (after twisting by
+        `|\det|^{j/2}` where `j` is the twist factor). Since local components
+        of modular forms are always tempered, this is a useful check on our
+        calculations.
+
+        EXAMPLE::
+
+            sage: Pi = LocalComponent(Newforms(DirichletGroup(21)([-1, 1]), 3, names='j')[0], 7)
+            sage: Pi.check_tempered()
+        """
+        c1 = self.characters()[0]
+        K = c1.base_ring()
+        p = self.prime()
+        w = QQbar(p)**(self.twist_factor() / ZZ(2))
+        for sigma in K.embeddings(QQbar):
+            assert sigma(c1(p)).abs() == w
+        
+class PrimitiveSupercuspidal(LocalComponentBase):
+    r"""
+    A primitive supercuspidal representation. Except for some excpetional cases
+    when `p = 2` which we do not implement here, such representations are
+    parametrized by smooth characters of tamely ramified quadratic extensions
+    of `\QQ_p`.
+
+    EXAMPLES::
+
+        sage: f = Newform("50a")
+        sage: Pi = LocalComponent(f, 5)
+        sage: type(Pi)
+        <class 'sage.modular.local_comp.local_comp.PrimitiveSupercuspidal'>
+        sage: Pi.species()
+        'Supercuspidal'
+        sage: TestSuite(Pi).run()
+    """
+    
+    def species(self):
+        r"""
+        The species of this local component, which is either 'Principal
+        Series', 'Special' or 'Supercuspidal'.
+        
+        EXAMPLE::
+            
+            sage: LocalComponent(Newform('49a'), 7).species()
+            'Supercuspidal'
+        """
+        return "Supercuspidal"
+
+    @cached_method
+    def type_space(self):
+        r"""
+        Return a :class:`~sage.modular.local_comp.type_space.TypeSpace` object
+        describing the (homological) type space of this newform, which we know
+        is dual to the type space of the local component.
+
+        EXAMPLE::
+
+            sage: LocalComponent(Newform('49a'), 7).type_space()
+            6-dimensional type space at prime 7 of form q + q^2 - q^4 + O(q^6)
+        """
+        return TypeSpace(self.newform(), self.prime())
+
+    def characters(self):
+        r"""
+        Return the two conjugate characters of `K^\times`, where `K` is some
+        quadratic extension of `\QQ_p`, defining this representation. This is
+        fully implemented only in the case where the power of `p` dividing the
+        level of the form is even, in which case `K` is the unique unramified
+        quadratic extension of `\QQ_p`.
+
+        EXAMPLES:
+
+        The first example from _[LW11]::
+
+            sage: f = Newform('50a')
+            sage: Pi = LocalComponent(f, 5)
+            sage: chars = Pi.characters(); chars
+            [
+            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> d, 5 |--> 1,
+            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -d - 1, 5 |--> 1
+            ]
+            sage: chars[0].base_ring()
+            Number Field in d with defining polynomial x^2 + x + 1
+        
+        These characters are interchanged by the Frobenius automorphism of `\mathbb{F}_{25}`::
+
+            sage: chars[0] == chars[1]**5
+            True
+
+        A more complicated example (higher weight and nontrivial central character)::
+            
+            sage: f = Newforms(GammaH(25, [6]), 3, names='j')[0]; f
+            q + j0*q^2 + 1/3*j0^3*q^3 - 1/3*j0^2*q^4 + O(q^6)
+            sage: Pi = LocalComponent(f, 5)
+            sage: Pi.characters()
+            [
+            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> d, 5 |--> 5,
+            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -d - 1/3*j0^3, 5 |--> 5
+            ]
+            sage: Pi.characters()[0].base_ring()
+            Number Field in d with defining polynomial x^2 + 1/3*j0^3*x - 1/3*j0^2 over its base field
+
+        .. warning::
+            
+            The above output isn't actually the same as in Example 2 of
+            _[LW11], due to an error in the published paper (correction
+            pending) -- the published paper has the inverses of the above
+            characters. 
+
+        A higher level example::
+
+            sage: f = Newform('81a', names='j'); f
+            q + j0*q^2 + q^4 - j0*q^5 + O(q^6)
+            sage: LocalComponent(f, 3).characters()
+            [
+            Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> -2*d - j0, 4 |--> 1, 3*s + 1 |--> -j0*d - 2, 3 |--> 1,
+            Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> 2*d + j0, 4 |--> 1, 3*s + 1 |--> j0*d + 1, 3 |--> 1
+            ]
+
+        In the ramified case, it's not fully implemented, and just returns a
+        string indicating which ramified extension is being considered::
+
+            sage: Pi = LocalComponent(Newform('27a'), 3)
+            sage: Pi.characters()
+            'Character of Q_3(sqrt(-3))'
+            sage: Pi = LocalComponent(Newform('54a'), 3)
+            sage: Pi.characters()
+            'Character of Q_3(sqrt(3))'
+        """
+        T = self.type_space()
+        if self.conductor() % 2 == 0:
+
+            G = SmoothCharacterGroupUnramifiedQuadratic(self.prime(), self.coefficient_field())
+            n = self.conductor() // 2
+            g = G.quotient_gen(n)
+            m = g.matrix().change_ring(ZZ).list()
+            tr = (~T.rho(m)).trace()
+            
+            # The inverse is needed here because T is the *homological* type space,
+            # which is dual to the cohomological one that defines the local component.
+
+            X = polygen(self.coefficient_field())
+            theta_poly = X**2 - (-1)**n*tr*X + self.central_character()(g.norm())
+            if theta_poly.is_irreducible():
+                F = self.coefficient_field().extension(theta_poly, "d")
+                G = G.base_extend(F)
+            chi1, chi2 = [G.extend_character(n, self.central_character(), x[0]) for x in theta_poly.roots(G.base_ring())]
+
+            # Consistency checks
+            assert chi1.restrict_to_Qp() == chi2.restrict_to_Qp() == self.central_character()
+            assert chi1*chi2 == chi1.parent().compose_with_norm(self.central_character())
+
+            return Sequence([chi1, chi2], check=False, cr=True)
+
+        else:
+            # The ramified case.
+            
+            p = self.prime()
+            
+            if p == 2: 
+                # The ramified 2-adic representations aren't classified by admissible pairs. Die.
+                raise NotImplementedError( "Computation with ramified 2-adic representations not implemented" )
+
+            if p % 4 == 3: 
+                a = ZZ(-1)
+            else: 
+                a = ZZ(Zmod(self.prime()).quadratic_nonresidue())
+
+            tr1 = (~T.rho([0,1,a*p, 0])).trace()
+            tr2 = (~T.rho([0,1,p,0])).trace()
+
+            if tr1 == tr2 == 0: 
+                # This *can* happen. E.g. if the central character satisfies
+                # chi(-1) = -1, then we have theta(pi) + theta(-pi) = theta(pi)
+                # * (1 + -1) = 0. In this case, one can presumably identify
+                # the character and the extension by some more subtle argument
+                # but I don't know of a good way to automate the process.
+                raise NotImplementedError( "Can't identify ramified quadratic extension -- both traces zero" )
+            elif tr1 == 0:
+                return "Character of Q_%s(sqrt(%s))" % (p, p)
+
+            elif tr2 == 0:
+                return "Character of Q_%s(sqrt(%s))" % (p, a*p)
+
+            else:
+                # At least one of the traces is *always* 0, since the type
+                # space has to be isomorphic to its twist by the (ramified
+                # quadratic) character corresponding to the quadratic
+                # extension. 
+                raise RuntimeError( "Can't get here!" )
+
+    def check_tempered(self):
+        r"""
+        Check that this representation is tempered (after twisting by
+        `|\det|^{j/2}` where `j` is the twist factor). Since local components
+        of modular forms are always tempered, this is a useful check on our
+        calculations.
+
+        Since the computation of the characters attached to this representation
+        is not implemented in the odd-conductor case, a NotImplementedError
+        will be raised for such representations.
+
+        EXAMPLE::
+
+            sage: LocalComponent(Newform("50a"), 5).check_tempered()
+            sage: LocalComponent(Newform("27a"), 3).check_tempered() # not tested
+        """
+        if self.conductor() % 2:
+            raise NotImplementedError 
+        c1, c2 = self.characters()
+        K = c1.base_ring()
+        p = self.prime()
+        w = QQbar(p)**(self.twist_factor() / ZZ(2))
+        for sigma in K.embeddings(QQbar):
+            assert c1(p).abs() == c2(p).abs() == w
diff --git a/sage/modular/local_comp/smoothchar.py b/sage/modular/local_comp/smoothchar.py
new file mode 100644
--- /dev/null
+++ b/sage/modular/local_comp/smoothchar.py
@@ -0,0 +1,1580 @@
+r"""
+Smooth characters of `p`-adic fields
+
+Let `F` be a finite extension of `\QQ_p`. Then we may consider the group of
+smooth (i.e. locally constant) group homomorphisms `F^\times \to L^\times`, for
+`L` any field. Such characters are important since they can be used to
+parametrise smooth representations of `\mathrm{GL}_2(\QQ_p)`, which arise as
+the local components of modular forms.
+
+This module contains classes to represent such characters when `F` is `\QQ_p`
+or a quadratic extension. In the latter case, we choose a quadratic extension
+`K` of `\QQ` whose completion at `p` is `F`, and use Sage's wrappers of the
+Pari ``idealstar`` and ``ideallog`` methods to work in the finite group
+`\mathcal{O}_K / p^c` for `c \ge 0`.
+
+An example with characters of `\QQ_7`::
+
+    sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+    sage: K.<z> = CyclotomicField(42)
+    sage: G = SmoothCharacterGroupQp(7, K)
+    sage: G.unit_gens(2), G.exponents(2)
+    ([3, 7], [42, 0])
+    
+The output of the last line means that the group `\QQ_7^\times / (1 + 7^2
+\ZZ_7)` is isomorphic to `C_{42} \times \ZZ`, with the two factors being
+generated by `3` and `7` respectively. We create a character by specifying the
+images of these generators::
+
+    sage: chi = G.character(2, [z^5, 11 + z]); chi
+    Character of Q_7*, of level 2, mapping 3 |--> z^5, 7 |--> z + 11
+    sage: chi(4)
+    z^8
+    sage: chi(42)
+    z^10 + 11*z^9
+
+Characters are themselves group elements, and basic arithmetic on them works::
+
+    sage: chi**3
+    Character of Q_7*, of level 2, mapping 3 |--> z^8 - z, 7 |--> z^3 + 33*z^2 + 363*z + 1331
+    sage: chi.multiplicative_order()
+    +Infinity
+"""
+
+import operator
+from sage.structure.element     import MultiplicativeGroupElement
+from sage.structure.parent_base import ParentWithBase
+from sage.structure.sequence    import Sequence
+from sage.rings.all             import QQ, ZZ, Zmod, NumberField, is_Ring
+from sage.misc.cachefunc        import cached_method
+from sage.misc.abstract_method  import abstract_method
+from sage.misc.misc_c           import prod
+from sage.categories.groups     import Groups
+from sage.functions.other       import ceil
+from sage.misc.mrange           import xmrange
+
+from sage.structure.element import FieldElement
+
+class SmoothCharacterGeneric(MultiplicativeGroupElement):
+    r"""
+    A smooth (i.e. locally constant) character of `F^\times`, for `F` some
+    finite extension of `\QQ_p`.
+    """
+    def __init__(self, parent, c, values_on_gens):
+        r"""
+        Standard init function.
+        
+        EXAMPLES::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: G = SmoothCharacterGroupUnramifiedQuadratic(2, QQ)
+            sage: G.character(0, [17]) # indirect doctest
+            Character of unramified extension Q_2(s)* (s^2 + s + 1 = 0), of level 0, mapping 2 |--> 17
+            sage: G.character(1, [1, 17]) # indirect doctest
+            Character of unramified extension Q_2(s)* (s^2 + s + 1 = 0), of level 0, mapping 2 |--> 17
+            sage: G.character(2, [1, -1, 1, 17]) # indirect doctest
+            Character of unramified extension Q_2(s)* (s^2 + s + 1 = 0), of level 2, mapping s |--> 1, 2*s + 1 |--> -1, -1 |--> 1, 2 |--> 17
+            sage: G.character(2, [1, 1, 1, 17]) # indirect doctest
+            Character of unramified extension Q_2(s)* (s^2 + s + 1 = 0), of level 0, mapping 2 |--> 17
+        """
+        MultiplicativeGroupElement.__init__(self, parent)
+        self._c = c
+        self._values_on_gens = values_on_gens
+        self._check_level()
+        
+    def _check_level(self):
+        r"""
+        Checks that this character has the level it claims to have, and if not,
+        decrement the level by 1. This is called by :meth:`__init__`.
+        
+        EXAMPLES::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: SmoothCharacterGroupQp(5, QQ).character(5, [-1, 7]) # indirect doctest
+            Character of Q_5*, of level 1, mapping 2 |--> -1, 5 |--> 7    
+        """
+        if self.level() == 0: return
+        v = self.parent().subgroup_gens(self.level())
+        if all([self(x) == 1 for x in v]):
+            new_gens = self.parent().unit_gens(self.level() - 1)
+            new_values = [self(x) for x in new_gens]
+            self._values_on_gens = Sequence(new_values, universe=self.base_ring(), immutable=True)
+            self._c = self._c - 1
+            self._check_level()
+          
+    def __cmp__(self, other):
+        r"""
+        Compare self and other. Note that this only gets called when the
+        parents of self and other are identical.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp, SmoothCharacterGroupUnramifiedQuadratic
+            sage: SmoothCharacterGroupQp(7, Zmod(3)).character(1, [2, 1]) == SmoothCharacterGroupQp(7, ZZ).character(1, [-1, 1])
+            True
+            sage: chi1 = SmoothCharacterGroupUnramifiedQuadratic(7, QQ).character(0, [1])
+            sage: chi2 = SmoothCharacterGroupQp(7, QQ).character(0, [1])
+            sage: chi1 == chi2
+            False
+            sage: chi2.parent()(chi1) == chi2
+            True
+            sage: chi1 == loads(dumps(chi1))
+            True
+        """
+        assert other.parent() is self.parent()
+        return cmp(self.level(), other.level()) or cmp(self._values_on_gens, other._values_on_gens)
+
+    def multiplicative_order(self):
+        r"""
+        Return the order of this character as an element of the character group.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: K.<z> = CyclotomicField(42)
+            sage: G = SmoothCharacterGroupQp(7, K)
+            sage: G.character(3, [z^10 - z^3, 11]).multiplicative_order()
+            +Infinity
+            sage: G.character(3, [z^10 - z^3, 1]).multiplicative_order()
+            42
+            sage: G.character(1, [z^7, z^14]).multiplicative_order()
+            6
+            sage: G.character(0, [1]).multiplicative_order()
+            1
+        """
+        from sage.rings.arith import lcm
+        from sage.rings.infinity import Infinity
+        if self._values_on_gens[-1].multiplicative_order() == Infinity:
+            return Infinity
+        else:
+            return lcm([x.multiplicative_order() for x in self._values_on_gens])
+        
+    def level(self):
+        r"""
+        Return the level of this character, i.e. the smallest integer `c \ge 0`
+        such that it is trivial on `1 + \mathfrak{p}^c`.
+        
+        EXAMPLES::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: SmoothCharacterGroupQp(7, QQ).character(2, [-1, 1]).level()
+            1
+        """
+        return self._c    
+        
+    def __call__(self, x):
+        r"""
+        Evaluate the character at ``x``, which should be a nonzero element of
+        the number field of the parent group.
+        
+        EXAMPLES::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: K.<z> = CyclotomicField(42)
+            sage: chi = SmoothCharacterGroupQp(7, K).character(3, [z^10 - z^3, 11])
+            sage: [chi(x) for x in [1, 2, 3, 9, 21, 1/12345678]]
+            [1, -z, z^10 - z^3, -z^11 - z^10 + z^8 + z^7 - z^6 - z^5 + z^3 + z^2 - 1, 11*z^10 - 11*z^3, z^7 - 1]
+        
+        Non-examples::
+            
+            sage: chi(QuadraticField(-1,'i').gen())
+            Traceback (most recent call last):
+            ...
+            TypeError: no canonical coercion from Number Field in i with defining polynomial x^2 + 1 to Rational Field
+            sage: chi(0)
+            Traceback (most recent call last):
+            ...
+            ValueError: cannot evaluate at zero
+            sage: chi(Mod(1, 12))
+            Traceback (most recent call last):
+            ...
+            TypeError: no canonical coercion from Ring of integers modulo 12 to Rational Field
+        
+        Some examples with an unramified quadratic extension, where the choice
+        of generators is arbitrary (but deterministic)::
+            
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: K.<z> = CyclotomicField(30)
+            sage: G = SmoothCharacterGroupUnramifiedQuadratic(5, K)
+            sage: chi = G.character(2, [z**5, z**(-6), z**6, 3]); chi
+            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 2, mapping 11*s - 10 |--> z^5, 6 |--> -z^7 - z^6 + z^3 + z^2 - 1, 5*s + 1 |--> z^6, 5 |--> 3
+            sage: chi(G.unit_gens(2)[0]**7 / G.unit_gens(2)[1]/5)
+            1/3*z^6 - 1/3*z
+            sage: chi(2)
+            -z^3
+        """
+        v = self.parent().discrete_log(self.level(), x)
+        return prod([self._values_on_gens[i] ** v[i] for i in xrange(len(v))])
+    
+    def _repr_(self):
+        r"""
+        String representation of this character.
+        
+        EXAMPLES::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: K.<z> = CyclotomicField(20)
+            sage: SmoothCharacterGroupQp(5, K).character(2, [z, z+1])._repr_()
+            'Character of Q_5*, of level 2, mapping 2 |--> z, 5 |--> z + 1'
+            
+        Examples over field extensions::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: K.<z> = CyclotomicField(15)
+            sage: SmoothCharacterGroupUnramifiedQuadratic(5, K).character(2, [z**5, z**3, 1, z+1])._repr_()
+            'Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 2, mapping 11*s - 10 |--> z^5, 6 |--> z^3, 5*s + 1 |--> 1, 5 |--> z + 1'
+        """
+        gens = self.parent().unit_gens(self.level())
+        mapst = ", ".join( str(gens[i]) + ' |--> ' + str(self._values_on_gens[i]) for i in range(len(gens)) )
+        return "Character of %s, of level %s, mapping %s" % (self.parent()._field_name(), self.level(), mapst)
+
+    def _mul_(self, other):
+        r"""
+        Product of self and other.
+        
+        EXAMPLES::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: K.<z> = CyclotomicField(20)
+            sage: chi1 = SmoothCharacterGroupQp(5, K).character(2, [z, z+1])
+            sage: chi2 = SmoothCharacterGroupQp(5, K).character(2, [z^4, 3])
+            sage: chi1 * chi2 # indirect doctest
+            Character of Q_5*, of level 1, mapping 2 |--> z^5, 5 |--> 3*z + 3
+            sage: chi2 * chi1 # indirect doctest
+            Character of Q_5*, of level 1, mapping 2 |--> z^5, 5 |--> 3*z + 3
+            sage: chi1 *  SmoothCharacterGroupQp(5, QQ).character(2, [-1, 7]) # indirect doctest
+            Character of Q_5*, of level 2, mapping 2 |--> -z, 5 |--> 7*z + 7
+        """
+        if other.level() > self.level():
+            return other * self
+        return self.parent().character(self.level(), [self(x) * other(x) for x in self.parent().unit_gens(self.level())])
+
+    def __invert__(self):
+        r"""
+        Multiplicative inverse of self.
+        
+        EXAMPLES::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: K.<z> = CyclotomicField(12)
+            sage: chi = SmoothCharacterGroupUnramifiedQuadratic(2, K).character(4, [z**4, z**3, z**9, -1, 7]); chi
+            Character of unramified extension Q_2(s)* (s^2 + s + 1 = 0), of level 4, mapping s |--> z^2 - 1, 2*s + 1 |--> z^3, 4*s + 1 |--> -z^3, -1 |--> -1, 2 |--> 7
+            sage: chi**(-1) # indirect doctest
+            Character of unramified extension Q_2(s)* (s^2 + s + 1 = 0), of level 4, mapping s |--> -z^2, 2*s + 1 |--> -z^3, 4*s + 1 |--> z^3, -1 |--> -1, 2 |--> 1/7
+            sage: SmoothCharacterGroupUnramifiedQuadratic(2, QQ).character(0, [7]) / chi # indirect doctest
+            Character of unramified extension Q_2(s)* (s^2 + s + 1 = 0), of level 4, mapping s |--> -z^2, 2*s + 1 |--> -z^3, 4*s + 1 |--> z^3, -1 |--> -1, 2 |--> 1
+        """
+        return self.parent().character(self.level(), [~self(x) for x in self.parent().unit_gens(self.level())]) 
+
+    def restrict_to_Qp(self):
+        r"""
+        Return the restriction of this character to `\QQ_p^\times`, embedded as
+        a subfield of `F^\times`.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupRamifiedQuadratic
+            sage: SmoothCharacterGroupRamifiedQuadratic(3, 0, QQ).character(0, [2]).restrict_to_Qp()
+            Character of Q_3*, of level 0, mapping 3 |--> 4
+        """
+        G = SmoothCharacterGroupQp(self.parent().prime(), self.base_ring())
+        ugs = G.unit_gens(self.level())
+        return G.character(self.level(), [self(x) for x in ugs])
+
+    def galois_conjugate(self):
+        r"""
+        Return the composite of this character with the order `2` automorphism of
+        `K / \QQ_p` (assuming `K` is quadratic).
+
+        Note that this is the Galois operation on the *domain*, not on the
+        *codomain*.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: K.<w> = CyclotomicField(3)
+            sage: G = SmoothCharacterGroupUnramifiedQuadratic(2, K)
+            sage: chi = G.character(2, [w, -1,-1, 3*w])
+            sage: chi2 = chi.galois_conjugate(); chi2
+            Character of unramified extension Q_2(s)* (s^2 + s + 1 = 0), of level 2, mapping s |--> -w - 1, 2*s + 1 |--> 1, -1 |--> -1, 2 |--> 3*w
+
+            sage: chi.restrict_to_Qp() == chi2.restrict_to_Qp()
+            True
+            sage: chi * chi2 == chi.parent().compose_with_norm(chi.restrict_to_Qp())
+            True
+        """
+        K,s = self.parent().number_field().objgen()
+        if K.absolute_degree() != 2: 
+            raise ValueError( "Character must be defined on a quadratic extension" )
+        sigs = K.embeddings(K)
+        sig = [x for x in sigs if x(s) != s][0]
+        return self.parent().character(self.level(), [self(sig(x)) for x in self.parent().unit_gens(self.level())])
+
+
+class SmoothCharacterGroupGeneric(ParentWithBase):
+    r"""
+    The group of smooth (i.e. locally constant) characters of a `p`-adic field,
+    with values in some ring `R`. This is an abstract base class and should not
+    be instantiated directly.
+    """
+
+    Element = SmoothCharacterGeneric
+
+    def __init__(self, p, base_ring):
+        r"""
+        TESTS::
+            
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupGeneric
+            sage: G = SmoothCharacterGroupGeneric(3, QQ)
+            sage: SmoothCharacterGroupGeneric(3, "hello")
+            Traceback (most recent call last):
+            ...
+            TypeError: base ring (=hello) must be a ring
+        """
+        if not is_Ring(base_ring):
+            raise TypeError( "base ring (=%s) must be a ring" % base_ring )
+        ParentWithBase.__init__(self, base=base_ring, category=Groups())
+        if not (p in ZZ and ZZ(p).is_prime()):
+            raise ValueError( "p (=%s) must be a prime integer" % p )
+        self._p = ZZ.coerce(p)
+    
+    def _element_constructor_(self, x):
+        r"""
+        Construct an element of this group from ``x`` (possibly noncanonically).
+        This only works if ``x`` is a character of a field containing the field of
+        self, whose values lie in a field that can be converted into self.
+
+        EXAMPLES::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: K.<i> = QuadraticField(-1)
+            sage: G = SmoothCharacterGroupQp(3, QQ)
+            sage: GK = SmoothCharacterGroupQp(3, K)
+            sage: chi = GK(G.character(0, [4])); chi # indirect doctest
+            Character of Q_3*, of level 0, mapping 3 |--> 4
+            sage: chi.parent() is GK
+            True
+            sage: G(GK.character(0, [7])) # indirect doctest
+            Character of Q_3*, of level 0, mapping 3 |--> 7
+            sage: G(GK.character(0, [i])) # indirect doctest
+            Traceback (most recent call last):
+            ...
+            TypeError: Unable to coerce i to a rational
+        """
+        if x == 1: 
+            return self.character(0, [1])
+        if hasattr(x, 'parent') \
+          and isinstance(x.parent(), SmoothCharacterGroupGeneric) \
+          and x.parent().number_field().has_coerce_map_from(self.number_field()):
+            return self.character(x.level(), [x(v) for v in self.unit_gens(x.level())])
+        else:
+            raise TypeError
+
+    def __cmp__(self, other):
+        r"""
+        TESTS::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: G = SmoothCharacterGroupQp(3, QQ)
+            sage: G == SmoothCharacterGroupQp(3, QQ[I])
+            False
+            sage: G == 7
+            False
+            sage: G == SmoothCharacterGroupQp(7, QQ)
+            False
+            sage: G == SmoothCharacterGroupQp(3, QQ)
+            True
+        """    
+        return cmp(type(self), type(other)) \
+            or cmp(self.prime(), other.prime()) \
+            or cmp(self.number_field(), other.number_field()) \
+            or cmp(self.base_ring(), other.base_ring())
+
+    def _coerce_map_from_(self, other):
+        r"""
+        Return True if self has a canonical coerce map from other.
+        
+        EXAMPLES::
+            
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: K.<i> = QuadraticField(-1)
+            sage: G = SmoothCharacterGroupQp(3, QQ)
+            sage: GK = SmoothCharacterGroupQp(3, K)
+            sage: G.has_coerce_map_from(GK)
+            False
+            sage: GK.has_coerce_map_from(G)
+            True
+            sage: GK.coerce(G.character(0, [4]))
+            Character of Q_3*, of level 0, mapping 3 |--> 4
+            sage: G.coerce(GK.character(0, [4]))
+            Traceback (most recent call last):
+            ...
+            TypeError: no canonical coercion from Group of smooth characters of Q_3* with values in Number Field in i with defining polynomial x^2 + 1 to Group of smooth characters of Q_3* with values in Rational Field
+            sage: G.character(0, [4]) in GK # indirect doctest
+            True
+        
+        The coercion framework handles base extension, so we test that too::
+        
+            sage: K.<i> = QuadraticField(-1)
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: G = SmoothCharacterGroupUnramifiedQuadratic(3, QQ)
+            sage: G.character(0, [1]).base_extend(K)
+            Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 0, mapping 3 |--> 1
+
+        """
+        if isinstance(other, SmoothCharacterGroupGeneric) \
+          and other.number_field() == self.number_field() \
+          and self.base_ring().has_coerce_map_from(other.base_ring()):
+            return True
+        else:
+            return False 
+        
+    def prime(self):
+        r"""
+        The residue characteristic of the underlying field.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupGeneric
+            sage: SmoothCharacterGroupGeneric(3, QQ).prime()
+            3
+        """
+        return self._p
+
+    @abstract_method
+    def change_ring(self, ring):
+        r"""
+        Return the character group of the same field, but with values in a
+        different coefficient ring. To be implemented by all derived classes
+        (since the generic base class can't know the parameters).
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupGeneric
+            sage: SmoothCharacterGroupGeneric(3, QQ).change_ring(ZZ)
+            Traceback (most recent call last):
+            ...
+            NotImplementedError: <abstract method change_ring at ...>
+        """
+        pass
+        
+    def base_extend(self, ring):
+        r"""
+        Return the character group of the same field, but with values in a new
+        coefficient ring into which the old coefficient ring coerces. An error
+        will be raised if there is no coercion map from the old coefficient
+        ring to the new one.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: G = SmoothCharacterGroupQp(3, QQ)
+            sage: G.base_extend(QQbar)
+            Group of smooth characters of Q_3* with values in Algebraic Field
+            sage: G.base_extend(Zmod(3))
+            Traceback (most recent call last):
+            ...
+            TypeError: no canonical coercion from Rational Field to Ring of integers modulo 3
+
+        """
+        if not ring.has_coerce_map_from(self.base_ring()) :
+            ring.coerce(self.base_ring().an_element()) 
+            # this is here to flush out errors
+        
+        return self.change_ring(ring)
+    
+    @abstract_method
+    def _field_name(self):
+        r"""
+        A string representing the name of the p-adic field of which this is the
+        character group. To be overridden by derived subclasses.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupGeneric
+            sage: SmoothCharacterGroupGeneric(3, QQ)._field_name()
+            Traceback (most recent call last):
+            ...
+            NotImplementedError: <abstract method _field_name at ...>
+        """
+        pass
+
+    def _repr_(self):
+        r"""
+        String representation of self.
+        
+        EXAMPLE::
+            
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: SmoothCharacterGroupQp(7, QQ)._repr_()
+            'Group of smooth characters of Q_7* with values in Rational Field'
+        """
+        return "Group of smooth characters of %s with values in %s" % (self._field_name(), self.base_ring())
+
+    @abstract_method
+    def ideal(self, level):
+        r"""
+        Return the ``level``-th power of the maximal ideal of the ring of
+        integers of the p-adic field. Since we approximate by using number
+        field arithmetic, what is actually returned is an ideal in a number
+        field.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupGeneric
+            sage: SmoothCharacterGroupGeneric(3, QQ).ideal(3)
+            Traceback (most recent call last):
+            ...
+            NotImplementedError: <abstract method ideal at ...>
+        """
+        pass
+
+    @abstract_method
+    def unit_gens(self, level):
+        r"""
+        A list of generators `x_1, \dots, x_d` of the abelian group `F^\times /
+        (1 + \mathfrak{p}^c)^\times`, where `c` is the given level, satisfying
+        no relations other than `x_i^{n_i} = 1` for each `i` (where the
+        integers `n_i` are returned by :meth:`exponents`). We adopt the
+        convention that the final generator `x_d` is a uniformiser (and `n_d =
+        0`).
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupGeneric
+            sage: SmoothCharacterGroupGeneric(3, QQ).unit_gens(3)
+            Traceback (most recent call last):
+            ...
+            NotImplementedError: <abstract method unit_gens at ...>
+        """
+        pass
+
+    @abstract_method
+    def exponents(self, level):
+        r"""
+        The orders `n_1, \dots, n_d` of the generators `x_i` of `F^\times / (1
+        + \mathfrak{p}^c)^\times` returned by :meth:`unit_gens`.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupGeneric
+            sage: SmoothCharacterGroupGeneric(3, QQ).exponents(3)
+            Traceback (most recent call last):
+            ...
+            NotImplementedError: <abstract method exponents at ...>
+        """
+        pass
+
+    @abstract_method
+    def subgroup_gens(self, level):
+        r"""
+        A set of elements of `(\mathcal{O}_F / \mathfrak{p}^c)^\times`
+        generating the kernel of the reduction map to `(\mathcal{O}_F /
+        \mathfrak{p}^{c-1})^\times`.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupGeneric
+            sage: SmoothCharacterGroupGeneric(3, QQ).subgroup_gens(3)
+            Traceback (most recent call last):
+            ...
+            NotImplementedError: <abstract method subgroup_gens at ...>
+        """
+        pass
+
+    @abstract_method
+    def discrete_log(self, level):
+        r"""
+        Given an element `x \in F^\times` (lying in the number field `K` of
+        which `F` is a completion, see module docstring), express the class of
+        `x` in terms of the generators of `F^\times / (1 +
+        \mathfrak{p}^c)^\times` returned by :meth:`unit_gens`.
+        
+        This should be overridden by all derived classes. The method should
+        first attempt to canonically coerce `x` into ``self.number_field()``,
+        and check that the result is not zero.
+        
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupGeneric
+            sage: SmoothCharacterGroupGeneric(3, QQ).discrete_log(3)
+            Traceback (most recent call last):
+            ...
+            NotImplementedError: <abstract method discrete_log at ...>
+        """
+        pass
+
+    def character(self, level, values_on_gens):
+        r"""
+        Return the unique character of the given level whose values on the
+        generators returned by ``self.unit_gens(level)`` are
+        ``values_on_gens``.
+        
+        INPUT:
+        
+        - ``level`` (integer) an integer `\ge 0`
+        - ``values_on_gens`` (sequence) a sequence of elements of length equal
+          to the length of ``self.unit_gens(level)``. The values should be
+          convertible (that is, possibly noncanonically) into the base ring of self; they
+          should all be units, and all but the last must be roots of unity (of
+          the orders given by ``self.exponents(level)``.
+        
+        .. note::
+            
+            The character returned may have level less than ``level`` in general.
+        
+        EXAMPLES::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: K.<z> = CyclotomicField(42)
+            sage: G = SmoothCharacterGroupQp(7, K)
+            sage: G.character(2, [z^6, 8])
+            Character of Q_7*, of level 2, mapping 3 |--> z^6, 7 |--> 8
+            sage: G.character(2, [z^7, 8])
+            Character of Q_7*, of level 1, mapping 3 |--> z^7, 7 |--> 8
+            
+        Non-examples::
+        
+            sage: G.character(1, [z, 1])
+            Traceback (most recent call last):
+            ...
+            ValueError: value on generator 3 (=z) should be a root of unity of order 6
+            sage: G.character(1, [1, 0])
+            Traceback (most recent call last):
+            ...
+            ValueError: value on uniformiser 7 (=0) should be a unit
+        
+        An example with a funky coefficient ring::
+        
+            sage: G = SmoothCharacterGroupQp(7, Zmod(9))
+            sage: G.character(1, [2, 2])
+            Character of Q_7*, of level 1, mapping 3 |--> 2, 7 |--> 2
+            sage: G.character(1, [2, 3])
+            Traceback (most recent call last):
+            ...
+            ValueError: value on uniformiser 7 (=3) should be a unit
+
+        TESTS::
+
+            sage: G.character(1, [2])
+            Traceback (most recent call last):
+            ...
+            AssertionError: 2 images must be given
+        """
+        S = Sequence(values_on_gens, universe=self.base_ring(), immutable=True)
+        assert len(S) == len(self.unit_gens(level)), "{0} images must be given".format(len(self.unit_gens(level)))
+        n = self.exponents(level)
+        for i in xrange(len(S)):
+            if n[i] != 0 and not S[i]**n[i] == 1: 
+                raise ValueError( "value on generator %s (=%s) should be a root of unity of order %s" % (self.unit_gens(level)[i], S[i], n[i]) )
+            elif n[i] == 0 and not S[i].is_unit():
+                raise ValueError( "value on uniformiser %s (=%s) should be a unit" % (self.unit_gens(level)[i], S[i]) )
+        return self.element_class(self, level, S)
+
+    def _an_element_(self):
+        r"""
+        Return an element of this group. Required by the coercion machinery.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: K.<z> = CyclotomicField(42)
+            sage: G = SmoothCharacterGroupQp(7, K)
+            sage: G.an_element() # indirect doctest
+            Character of Q_7*, of level 0, mapping 7 |--> z
+        """
+        return self.character(0, [self.base_ring().an_element()])
+
+
+
+    def _test_unitgens(self, **options):
+        r"""
+        Test that the generators returned by ``unit_gens`` are consistent with
+        the exponents returned by ``exponents``.
+
+        EXAMPLE::
+            
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: SmoothCharacterGroupUnramifiedQuadratic(2, Zmod(8))._test_unitgens()
+        """
+        T = self._tester(**options)
+        for c in xrange(6):
+            gens = self.unit_gens(c)
+            exps = self.exponents(c)
+            T.assert_(exps[-1] == 0)
+            T.assert_(all([u != 0 for u in exps[:-1]]))
+            T.assert_(all([u.parent() is self.number_field() for u in gens]))
+
+            I = self.ideal(c)
+            for i in xrange(len(exps[:-1])):
+                g = gens[i]
+                for m in xrange(1, exps[i]):
+                    if (g - 1 in I):
+                        T.fail("For generator g=%s, g^%s = %s = 1 mod I, but order should be %s" % (gens[i], m, g, exps[i]))
+                    g = g * gens[i]
+                    # reduce g mod I
+                    if hasattr(I, "small_residue"):
+                        g = I.small_residue(g)
+                    else: # I is an ideal of ZZ 
+                        g = g % (I.gen())
+                if not (g - 1 in I):
+                    T.fail("For generator g=%s, g^%s = %s, which is not 1 mod I" % (gens[i], exps[i], g))
+            I = self.prime() if self.number_field() == QQ else self.ideal(1)
+            T.assert_(gens[-1].valuation(I) == 1)
+
+            # This implicitly tests that the gens really are gens!
+            _ = self.discrete_log(c, -1)
+
+    def _test_subgroupgens(self, **options):
+        r"""
+        Test that the values returned by :meth:`~subgroup_gens` are valid.
+
+        EXAMPLE::
+            
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: SmoothCharacterGroupQp(2, CC)._test_subgroupgens()
+        """
+        T = self._tester(**options)
+        for c in xrange(1, 6):
+            sgs = self.subgroup_gens(c)
+            I2 = self.ideal(c-1)
+            T.assert_(all([x-1 in I2 for x in sgs]), "Kernel gens at level %s not in kernel!" % c)
+
+            # now find the exponent of the kernel
+            
+            n1 = prod(self.exponents(c)[:-1])
+            n2 = prod(self.exponents(c-1)[:-1])
+            n = n1 // n2
+            # if c > 1, n will be a prime here, so that logs below gets calculated correctly
+            
+            logs = []
+            for idx in xmrange(len(sgs)*[n]):
+                y = prod( map(operator.pow, sgs, idx) )
+                L = tuple(self.discrete_log(c, y))
+                if L not in logs:
+                    logs.append(L)
+            T.assert_(n2 * len(logs) == n1, "Kernel gens at level %s don't generate everything!" % c)
+
+    def compose_with_norm(self, chi):
+        r"""
+        Calculate the character of `K^\times` given by `\chi \circ \mathrm{Norm}_{K/\QQ_p}`. 
+        Here `K` should be a quadratic extension and `\chi` a character of `\QQ_p^\times`.
+
+        EXAMPLE:
+
+        When `K` is the unramified quadratic extension, the level of the new character is the same as the old::
+
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp, SmoothCharacterGroupRamifiedQuadratic, SmoothCharacterGroupUnramifiedQuadratic
+            sage: K.<w> = CyclotomicField(6)
+            sage: G = SmoothCharacterGroupQp(3, K)
+            sage: chi = G.character(2, [w, 5])
+            sage: H = SmoothCharacterGroupUnramifiedQuadratic(3, K)
+            sage: H.compose_with_norm(chi)
+            Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> -1, 4 |--> -w, 3*s + 1 |--> w - 1, 3 |--> 25
+
+        In ramified cases, the level of the new character may be larger:
+
+        .. link 
+        
+        ::
+
+            sage: H = SmoothCharacterGroupRamifiedQuadratic(3, 0, K)
+            sage: H.compose_with_norm(chi)
+            Character of ramified extension Q_3(s)* (s^2 - 3 = 0), of level 3, mapping 2 |--> w - 1, s + 1 |--> -w, s |--> -5
+
+        On the other hand, since norm is not surjective, the result can even be trivial:
+        
+        .. link 
+        
+        ::
+
+            sage: chi = G.character(1, [-1, -1]); chi
+            Character of Q_3*, of level 1, mapping 2 |--> -1, 3 |--> -1
+            sage: H.compose_with_norm(chi)
+            Character of ramified extension Q_3(s)* (s^2 - 3 = 0), of level 0, mapping s |--> 1
+        """
+        if chi.parent().number_field() != QQ: raise ValueError
+        if self.number_field().absolute_degree() != 2: raise ValueError
+        n = chi.level()
+        P = chi.parent().prime() ** n
+        m = self.number_field()(P).valuation(self.ideal(1))
+        return self.character(m, [chi(x.norm(QQ)) for x in self.unit_gens(m)])
+
+class SmoothCharacterGroupQp(SmoothCharacterGroupGeneric):
+    r"""
+    The group of smooth characters of `\QQ_p^\times`, with values in some fixed
+    base ring.
+   
+    EXAMPLES::
+
+        sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+        sage: G = SmoothCharacterGroupQp(7, QQ); G
+        Group of smooth characters of Q_7* with values in Rational Field
+        sage: TestSuite(G).run()
+        sage: G == loads(dumps(G))
+        True
+    """
+    def unit_gens(self, level):
+        r"""
+        Return a set of generators `x_1, \dots, x_d` for `\QQ_p^\times / (1 +
+        p^c \ZZ_p)^\times`. These must be independent in the sense that there
+        are no relations between them other than relations of the form
+        `x_i^{n_i} = 1`. They need not, however, be in Smith normal form.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: SmoothCharacterGroupQp(7, QQ).unit_gens(3)
+            [3, 7]
+            sage: SmoothCharacterGroupQp(2, QQ).unit_gens(4)
+            [15, 5, 2]
+        """
+        if level == 0: 
+            return [QQ(self.prime())]
+        else: 
+            return [QQ(x) for x in Zmod(self.prime()**level).unit_gens()] + [QQ(self.prime())]
+
+    def exponents(self, level):
+        r"""
+        Return the exponents of the generators returned by :meth:`unit_gens`.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: SmoothCharacterGroupQp(7, QQ).exponents(3)
+            [294, 0]
+            sage: SmoothCharacterGroupQp(2, QQ).exponents(4)
+            [2, 4, 0]
+        """
+        if level == 0: return [0]
+        return [x.multiplicative_order() for x in Zmod(self.prime()**level).unit_gens()] + [0]
+        
+    def change_ring(self, ring):
+        r"""
+        Return the group of characters of the same field but with values in a
+        different ring. This need not have anything to do with the original
+        base ring, and in particular there won't generally be a coercion map
+        from self to the new group -- use
+        :meth:`~SmoothCharacterGroupGeneric.base_extend` if you want this.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: SmoothCharacterGroupQp(7, Zmod(3)).change_ring(CC)
+            Group of smooth characters of Q_7* with values in Complex Field with 53 bits of precision
+        """
+        return SmoothCharacterGroupQp(self.prime(), ring)
+            
+    def number_field(self):
+        r"""
+        Return the number field used for calculations (a dense subfield of the
+        local field of which this is the character group). In this case, this
+        is always the rational field.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: SmoothCharacterGroupQp(7, Zmod(3)).number_field()
+            Rational Field
+        """
+        return QQ
+    
+    def ideal(self, level):
+        r"""
+        Return the ``level``-th power of the maximal ideal. Since we
+        approximate by using rational arithmetic, what is actually returned is
+        an ideal of `\ZZ`.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: SmoothCharacterGroupQp(7, Zmod(3)).ideal(2)
+            Principal ideal (49) of Integer Ring
+        """
+        return ZZ.ideal(self.prime() ** level)
+
+    def _field_name(self):
+        r"""
+        Return a string representation of the field unit group of which this is
+        the character group.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: SmoothCharacterGroupQp(7, Zmod(3))._field_name()
+            'Q_7*'
+        """
+        return "Q_%s*" % self.prime()
+        
+    def discrete_log(self, level, x):
+        r"""
+        Express the class of `x` in `\QQ_p^\times / (1 + p^c)^\times` in terms
+        of the generators returned by :meth:`unit_gens`.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: G = SmoothCharacterGroupQp(7, QQ)
+            sage: G.discrete_log(0, 14)
+            [1]
+            sage: G.discrete_log(1, 14)
+            [2, 1]
+            sage: G.discrete_log(5, 14)
+            [9308, 1]
+        """
+        x = self.number_field().coerce(x)
+        if x == 0: raise ValueError( "cannot evaluate at zero" )
+        s = x.valuation(self.prime())
+        return Zmod(self.prime()**level)(x / self.prime()**s).generalised_log() + [s]
+        
+    def subgroup_gens(self, level):
+        r"""
+        Return a list of generators for the kernel of the map `(\ZZ_p / p^c)^\times
+        \to (\ZZ_p / p^{c-1})^\times`. 
+        
+        INPUT:
+        
+        - ``c`` (integer) an integer `\ge 1`
+        
+        EXAMPLES::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp
+            sage: G = SmoothCharacterGroupQp(7, QQ)
+            sage: G.subgroup_gens(1)
+            [3]
+            sage: G.subgroup_gens(2)
+            [8]
+            
+            sage: G = SmoothCharacterGroupQp(2, QQ)
+            sage: G.subgroup_gens(1)
+            []
+            sage: G.subgroup_gens(2)
+            [3]
+            sage: G.subgroup_gens(3)
+            [5]
+        """
+        if level == 0: 
+            raise ValueError
+        elif level == 1:
+            return self.unit_gens(level)[:-1]
+        else:
+            return [1 + self.prime()**(level - 1)]
+
+class SmoothCharacterGroupUnramifiedQuadratic(SmoothCharacterGroupGeneric):
+    r"""
+    The group of smooth characters of `\QQ_{p^2}^\times`, where `\QQ_{p^2}` is
+    the unique unramified quadratic extension of `\QQ_p`. We represent
+    `\QQ_{p^2}^\times` internally as the completion at the prime above `p` of a
+    quadratic number field, defined by (the obvious lift to `\ZZ` of) the
+    Conway polynomial modulo `p` of degree 2.
+    
+    EXAMPLE::
+    
+        sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+        sage: G = SmoothCharacterGroupUnramifiedQuadratic(3, QQ); G
+        Group of smooth characters of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0) with values in Rational Field
+        sage: G.unit_gens(3)
+        [-11*s, 4, 3*s + 1, 3]
+        sage: TestSuite(G).run()
+        sage: TestSuite(SmoothCharacterGroupUnramifiedQuadratic(2, QQ)).run()
+    """
+    
+    def __init__(self, prime, base_ring, names='s'):
+        r"""
+        Standard initialisation function.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: G = SmoothCharacterGroupUnramifiedQuadratic(3, QQ, 'foo'); G
+            Group of smooth characters of unramified extension Q_3(foo)* (foo^2 + 2*foo + 2 = 0) with values in Rational Field
+            sage: G == loads(dumps(G))
+            True
+        """
+        SmoothCharacterGroupGeneric.__init__(self, prime, base_ring)
+        self._name = names
+
+    def change_ring(self, ring):
+        r"""
+        Return the character group of the same field, but with values in a
+        different coefficient ring. This need not have anything to do with the
+        original base ring, and in particular there won't generally be a
+        coercion map from self to the new group -- use :meth:`base_extend` if
+        you want this.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: SmoothCharacterGroupUnramifiedQuadratic(7, Zmod(3), names='foo').change_ring(CC)
+            Group of smooth characters of unramified extension Q_7(foo)* (foo^2 + 6*foo + 3 = 0) with values in Complex Field with 53 bits of precision
+        """
+        # We want to make sure that both G and the base-extended version have
+        # the same values in the cache.
+        from copy import copy
+        G = SmoothCharacterGroupUnramifiedQuadratic(self.prime(), ring, self._name)
+        try:
+            G._cache___ideal = copy(self._cache___ideal)
+        except AttributeError:
+            pass
+        return G
+
+    def _field_name(self):
+        r"""
+        A string representing the unit group of which this is the character group.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: SmoothCharacterGroupUnramifiedQuadratic(7, Zmod(3), 'a')._field_name()
+            'unramified extension Q_7(a)* (a^2 + 6*a + 3 = 0)'
+        """
+        return "unramified extension Q_%s(%s)* (%s = 0)" % (self.prime(), self._name, self.number_field().polynomial().change_variable_name(self._name))
+
+    def number_field(self):
+        r"""
+        Return a number field of which this is the completion at `p`, defined by a polynomial
+        whose discriminant is not divisible by `p`.
+        
+        EXAMPLES::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: SmoothCharacterGroupUnramifiedQuadratic(7, QQ, 'a').number_field()
+            Number Field in a with defining polynomial x^2 + 6*x + 3
+            sage: SmoothCharacterGroupUnramifiedQuadratic(5, QQ, 'b').number_field()
+            Number Field in b with defining polynomial x^2 + 4*x + 2
+            sage: SmoothCharacterGroupUnramifiedQuadratic(2, QQ, 'c').number_field()
+            Number Field in c with defining polynomial x^2 + x + 1
+        """
+        from sage.rings.all import conway_polynomial, PolynomialRing
+        fbar = conway_polynomial(self.prime(), 2)
+        f = PolynomialRing(QQ,'x')([a.lift() for a in fbar])
+        return NumberField(f, self._name)
+
+    @cached_method
+    def ideal(self, c):
+        r"""
+        Return the ideal `p^c` of ``self.number_field()``. The result is
+        cached, since we use the methods
+        :meth:`~sage.rings.number_field.number_field_ideal.idealstar` and
+        :meth:`~sage.rings.number_field.number_field_ideal.ideallog` which
+        cache a Pari ``bid`` structure.
+        
+        EXAMPLES::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: G = SmoothCharacterGroupUnramifiedQuadratic(7, QQ, 'a'); I = G.ideal(3); I
+            Fractional ideal (343)
+            sage: I is G.ideal(3)
+            True
+        """
+        return self.number_field().ideal(self.prime()**c)
+    
+    @cached_method
+    def unit_gens(self, c):
+        r"""
+        A list of generators `x_1, \dots, x_d` of the abelian group `F^\times /
+        (1 + \mathfrak{p}^c)^\times`, where `c` is the given level, satisfying
+        no relations other than `x_i^{n_i} = 1` for each `i` (where the
+        integers `n_i` are returned by :meth:`exponents`). We adopt the
+        convention that the final generator `x_d` is a uniformiser (and `n_d =
+        0`).
+        
+        ALGORITHM: Use Teichmueller lifts.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: SmoothCharacterGroupUnramifiedQuadratic(7, QQ).unit_gens(0)
+            [7]
+            sage: SmoothCharacterGroupUnramifiedQuadratic(7, QQ).unit_gens(1)
+            [s, 7]
+            sage: SmoothCharacterGroupUnramifiedQuadratic(7, QQ).unit_gens(2)
+            [22*s, 8, 7*s + 1, 7]
+            sage: SmoothCharacterGroupUnramifiedQuadratic(7, QQ).unit_gens(3)
+            [169*s + 49, 8, 7*s + 1, 7]
+            
+        In the 2-adic case there can be more than 4 generators::
+        
+            sage: SmoothCharacterGroupUnramifiedQuadratic(2, QQ).unit_gens(0)
+            [2]
+            sage: SmoothCharacterGroupUnramifiedQuadratic(2, QQ).unit_gens(1)
+            [s, 2]
+            sage: SmoothCharacterGroupUnramifiedQuadratic(2, QQ).unit_gens(2)
+            [s, 2*s + 1, -1, 2]
+            sage: SmoothCharacterGroupUnramifiedQuadratic(2, QQ).unit_gens(3)
+            [s, 2*s + 1, 4*s + 1, -1, 2]
+        """
+        # special cases
+
+        p = self.prime()
+        K = self.number_field()
+        a = K.gen()
+
+        if c == 0:
+            return [K(p)]
+        elif c == 1:
+            return [a, K(p)]
+        elif p == 2:
+            if c == 2:
+                return [a, 1 + 2*a, K(-1), K(2)]
+            else:
+                return [a, 1 + 2*a, 1 + 4*a, K(-1), K(2)]
+
+        # general case
+
+        b = a
+        I = self.ideal(c)
+        
+        while b**(p**2 - 1) - 1 not in I:
+            b = I.reduce(b**(self.prime()**2))
+        return [b, K(1 + p), 1 + a*p, K(p)]
+
+    def exponents(self, c):
+        r"""
+        The orders `n_1, \dots, n_d` of the generators `x_i` of `F^\times / (1
+        + \mathfrak{p}^c)^\times` returned by :meth:`unit_gens`.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: SmoothCharacterGroupUnramifiedQuadratic(7, QQ).exponents(2)
+            [48, 7, 7, 0]
+            sage: SmoothCharacterGroupUnramifiedQuadratic(2, QQ).exponents(3)
+            [3, 4, 2, 2, 0]
+            sage: SmoothCharacterGroupUnramifiedQuadratic(2, QQ).exponents(2)
+            [3, 2, 2, 0]
+        """
+        p = self.prime()
+        if c == 0: return [0]
+        elif c == 1: return [p**2 - 1, 0]
+        elif p == 2 and c >= 3:
+            return [p**2 - 1, p**(c-1), p**(c-2), 2, 0]
+        else: return [p**2 - 1, p**(c-1), p**(c-1),0] 
+    
+    def subgroup_gens(self, level):
+        r"""
+        A set of elements of `(\mathcal{O}_F / \mathfrak{p}^c)^\times`
+        generating the kernel of the reduction map to `(\mathcal{O}_F /
+        \mathfrak{p}^{c-1})^\times`.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: SmoothCharacterGroupUnramifiedQuadratic(7, QQ).subgroup_gens(1)
+            [s]
+            sage: SmoothCharacterGroupUnramifiedQuadratic(7, QQ).subgroup_gens(2)
+            [8, 7*s + 1]
+            sage: SmoothCharacterGroupUnramifiedQuadratic(2, QQ).subgroup_gens(2)
+            [3, 2*s + 1]
+        """
+        if level == 0: 
+            raise ValueError
+        elif level == 1:
+            return self.unit_gens(level)[:-1]
+        else:
+            return [1 + self.prime()**(level - 1), 1 + self.prime()**(level - 1) * self.number_field().gen()]
+   
+    def quotient_gen(self, level):
+        r"""
+        Find an element generating the quotient
+
+        .. math::
+
+            \mathcal{O}_F^\times / \ZZ_p^\times \cdot (1 + p^c \mathcal{O}_F),
+
+        where `c` is the given level.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: G = SmoothCharacterGroupUnramifiedQuadratic(7,QQ)
+            sage: G.quotient_gen(1)
+            s
+            sage: G.quotient_gen(2)
+            -20*s - 21
+            sage: G.quotient_gen(3)
+            -69*s - 70
+
+        For `p = 2` an error will be raised for level `\ge 3`, as the quotient is not cyclic::
+
+            sage: G = SmoothCharacterGroupUnramifiedQuadratic(2,QQ)
+            sage: G.quotient_gen(1)
+            s
+            sage: G.quotient_gen(2)
+            -s + 2
+            sage: G.quotient_gen(3)
+            Traceback (most recent call last):
+            ...
+            ValueError: Quotient group not cyclic
+        """
+        if level == 0:
+            raise ValueError( "Quotient group is trivial" )
+        elif self.prime() == 2 and level >= 3:
+            raise ValueError( "Quotient group not cyclic" )
+        elif level == 1:
+            return self.unit_gens(level)[0]
+        else:
+            return self.ideal(level).reduce(self.unit_gens(level)[0] * (1 + self.prime() * self.number_field().gen()))
+
+    def extend_character(self, level, chi, x, check=True):
+        r"""
+        Return the unique character of `F^\times` which coincides with `\chi`
+        on `\QQ_p^\times` and maps the generator `\alpha` returned by
+        :meth:`quotient_gen` to `x`.
+
+        INPUT:
+
+        - ``chi``: a smooth character of `\QQ_p`, where `p` is the residue
+          characteristic of `F`, with values in the base ring of self (or some
+          other ring coercible to it)
+        - ``level``: the level of the new character (which should be at least
+          the level of ``chi``)
+        - ``x``: an element of the base ring of self (or some other ring
+          coercible to it).
+
+        A ``ValueError`` will be raised if `x^t \ne \chi(\alpha^t)`, where `t`
+        is the smallest integer such that `\alpha^t` is congruent modulo
+        `p^{\rm level}` to an element of `\QQ_p`.
+
+        EXAMPLES:
+
+        We extend an unramified character of `\QQ_3^\times` to the unramified
+        quadratic extension in various ways.
+
+        ::
+
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp, SmoothCharacterGroupUnramifiedQuadratic
+            sage: chi = SmoothCharacterGroupQp(5, QQ).character(0, [7]); chi
+            Character of Q_5*, of level 0, mapping 5 |--> 7
+            sage: G = SmoothCharacterGroupUnramifiedQuadratic(5, QQ)
+            sage: G.extend_character(1, chi, -1)
+            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -1, 5 |--> 7
+            sage: G.extend_character(2, chi, -1)
+            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -1, 5 |--> 7
+            sage: G.extend_character(3, chi, 1)
+            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 0, mapping 5 |--> 7
+            sage: K.<z> = CyclotomicField(6); G.base_extend(K).extend_character(1, chi, z)
+            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> z, 5 |--> 7
+
+        We extend the nontrivial quadratic character::
+
+            sage: chi = SmoothCharacterGroupQp(5, QQ).character(1, [-1, 7])
+            sage: K.<z> = CyclotomicField(24); G.base_extend(K).extend_character(1, chi, z^6)
+            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> z^6, 5 |--> 7
+
+        Extensions of higher level::
+
+            sage: K.<z> = CyclotomicField(20); rho = G.base_extend(K).extend_character(2, chi, z); rho
+            Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 2, mapping 11*s - 10 |--> z^5, 6 |--> 1, 5*s + 1 |--> -z^6, 5 |--> 7
+            sage: rho(3)
+            -1
+
+        Examples where it doesn't work::
+            
+            sage: G.extend_character(1, chi, 1)
+            Traceback (most recent call last):
+            ...
+            ValueError: Value at s must satisfy x^6 = chi(2) = -1, but it does not
+        
+            sage: G = SmoothCharacterGroupQp(2, QQ); H = SmoothCharacterGroupUnramifiedQuadratic(2, QQ)
+            sage: chi = G.character(3, [1, -1, 7])
+            sage: H.extend_character(2, chi, -1)
+            Traceback (most recent call last):
+            ...
+            ValueError: Level of extended character cannot be smaller than level of character of Qp
+        """
+        chi = chi.base_extend(self.base_ring())
+        if chi.level() > level: 
+            raise ValueError, "Level of extended character cannot be smaller than level of character of Qp"
+
+        # check it makes sense
+        e = (self.prime() + 1) * (self.prime()**(level - 1))
+        v = self.ideal(level).reduce(self.quotient_gen(level) ** e)
+
+        v = QQ(v)
+        if x**e != chi(v):
+            raise ValueError( "Value at %s must satisfy x^%s = chi(%s) = %s, but it does not" % (self.quotient_gen(level), e, v, chi(v)) )
+
+        # now do the calculation
+        values_on_standard_gens = []
+        other_gens = [self.quotient_gen(level)] + [ZZ(z) for z in Zmod(self.prime()**level).unit_gens()]
+        values_on_other_gens = [x] + [chi(u) for u in other_gens[1:]]
+        for s in self.unit_gens(level)[:-1]:
+            t = self.ideal(level).ideallog(s, other_gens)
+            values_on_standard_gens.append( prod([values_on_other_gens[i] ** t[i] for i in xrange(len(t))]) )
+        values_on_standard_gens.append(chi(self.prime()))
+        chiE = self.character(level, values_on_standard_gens)
+
+        # check it makes sense (optional but on by default)
+        if check:
+            assert chiE(self.quotient_gen(level)) == x
+            assert chiE.restrict_to_Qp() == chi
+
+        return chiE
+
+    def discrete_log(self, level, x):
+        r"""
+        Express the class of `x` in `F^\times / (1 + \mathfrak{p}^c)^\times` in
+        terms of the generators returned by ``self.unit_gens(level)``.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic
+            sage: G = SmoothCharacterGroupUnramifiedQuadratic(2, QQ)
+            sage: G.discrete_log(0, 12)
+            [2]
+            sage: G.discrete_log(1, 12)
+            [0, 2]
+            sage: v = G.discrete_log(5, 12); v
+            [0, 2, 0, 1, 2]
+            sage: g = G.unit_gens(5); prod([g[i]**v[i] for i in [0..4]])/12 - 1 in G.ideal(5)
+            True
+            sage: G.discrete_log(3,G.number_field()([1,1]))
+            [2, 0, 0, 1, 0]
+            sage: H = SmoothCharacterGroupUnramifiedQuadratic(5, QQ)
+            sage: x = H.number_field()([1,1]); x
+            s + 1
+            sage: v = H.discrete_log(5, x); v
+            [22, 263, 379, 0]
+            sage: h = H.unit_gens(5); prod([h[i]**v[i] for i in [0..3]])/x - 1 in H.ideal(5)
+            True
+        """
+        x = self.number_field().coerce(x)
+        if x == 0: raise ValueError( "cannot evaluate at zero" )
+        n1 = x.valuation(self.number_field().ideal(self.prime()))
+        x1 = x / self.prime() ** n1
+        if level == 0:
+            return [n1]
+        else:
+            return self.ideal(level).ideallog(x1, self.unit_gens(level)[:-1]) + [n1]
+
+
+class SmoothCharacterGroupRamifiedQuadratic(SmoothCharacterGroupGeneric):
+    r"""
+    The group of smooth characters of `K^\times`, where `K` is a ramified
+    quadratic extension of `\QQ_p`, and `p \ne 2`.
+    """
+    def __init__(self, prime, flag, base_ring, names='s'):
+        r"""
+        Standard initialisation function.
+
+        INPUT:
+
+        - ``prime`` -- a prime integer
+        - ``flag`` -- either 0 or 1
+        - ``base_ring`` -- a ring
+        - ``names`` -- a variable name (default ``s``)
+
+        If ``flag`` is 0, return the group of characters of the multiplicative
+        group of the field `\QQ_p(\sqrt{p})`. If ``flag`` is 1, use the
+        extension `\QQ_p(\sqrt{dp})`, where `d` is `-1` (if `p = 3 \pmod 4`) or
+        the smallest positive quadratic nonresidue mod `p` otherwise.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupRamifiedQuadratic
+            sage: G1 = SmoothCharacterGroupRamifiedQuadratic(3, 0, QQ); G1
+            Group of smooth characters of ramified extension Q_3(s)* (s^2 - 3 = 0) with values in Rational Field
+            sage: G2 = SmoothCharacterGroupRamifiedQuadratic(3, 1, QQ); G2
+            Group of smooth characters of ramified extension Q_3(s)* (s^2 + 3 = 0) with values in Rational Field
+            sage: G3 = SmoothCharacterGroupRamifiedQuadratic(5, 1, QQ); G3
+            Group of smooth characters of ramified extension Q_5(s)* (s^2 - 10 = 0) with values in Rational Field
+
+        TESTS:
+        
+        .. link 
+        
+        ::
+        
+            sage: TestSuite(G1).run()
+            sage: TestSuite(G2).run()
+            sage: TestSuite(G3).run()
+        """
+        if prime == 2: raise NotImplementedError( "Wildly ramified extensions not supported" )
+        SmoothCharacterGroupGeneric.__init__(self, prime, base_ring)
+        self._name = names
+        if flag not in [0, 1]:
+            raise ValueError( "Flag must be 0 (for Qp(sqrt(p)) ) or 1 (for the other ramified extension)" )
+        self._flag = flag
+        if flag == 0:
+            self._unif_sqr = self.prime()
+        else:
+            if self.prime() % 4 == 3:
+                self._unif_sqr = -self.prime()
+            else:
+                self._unif_sqr = ZZ(Zmod(self.prime()).quadratic_nonresidue()) * self.prime()
+
+    def change_ring(self, ring):
+        r"""
+        Return the character group of the same field, but with values in a
+        different coefficient ring. This need not have anything to do with the
+        original base ring, and in particular there won't generally be a
+        coercion map from self to the new group -- use :meth:`base_extend` if
+        you want this.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupRamifiedQuadratic
+            sage: SmoothCharacterGroupRamifiedQuadratic(7, 1, Zmod(3), names='foo').change_ring(CC)
+            Group of smooth characters of ramified extension Q_7(foo)* (foo^2 + 7 = 0) with values in Complex Field with 53 bits of precision
+        """
+        return SmoothCharacterGroupRamifiedQuadratic(self.prime(), self._flag, ring, self._name)
+
+    def _field_name(self):
+        r"""
+        A string representing the unit group of which this is the character group.
+        
+        EXAMPLE::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupRamifiedQuadratic
+            sage: SmoothCharacterGroupRamifiedQuadratic(7, 0, Zmod(3), 'a')._field_name()
+            'ramified extension Q_7(a)* (a^2 - 7 = 0)'
+        """
+        return "ramified extension Q_%s(%s)* (%s = 0)" % (self.prime(), self._name, self.number_field().polynomial().change_variable_name(self._name))
+
+    def number_field(self):
+        r"""
+        Return a number field of which this is the completion at `p`. 
+
+        EXAMPLES::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupRamifiedQuadratic
+            sage: SmoothCharacterGroupRamifiedQuadratic(7, 0, QQ, 'a').number_field()
+            Number Field in a with defining polynomial x^2 - 7
+            sage: SmoothCharacterGroupRamifiedQuadratic(5, 1, QQ, 'b').number_field()
+            Number Field in b with defining polynomial x^2 - 10
+            sage: SmoothCharacterGroupRamifiedQuadratic(7, 1, Zmod(6), 'c').number_field()
+            Number Field in c with defining polynomial x^2 + 7
+        """
+        from sage.rings.all import PolynomialRing
+        R, x = PolynomialRing(QQ, 'x').objgen()
+        f = x**2 - self._unif_sqr
+        return NumberField(f, self._name)
+
+    @cached_method
+    def ideal(self, c):
+        r"""
+        Return the ideal `p^c` of ``self.number_field()``. The result is
+        cached, since we use the methods
+        :meth:`~sage.rings.number_field.number_field_ideal.idealstar` and
+        :meth:`~sage.rings.number_field.number_field_ideal.ideallog` which
+        cache a Pari ``bid`` structure.
+        
+        EXAMPLES::
+        
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupRamifiedQuadratic
+            sage: G = SmoothCharacterGroupRamifiedQuadratic(5, 1, QQ, 'a'); I = G.ideal(3); I
+            Fractional ideal (25, 5*a)
+            sage: I is G.ideal(3)
+            True
+        """
+        return self.number_field().ideal([self.prime(), self.number_field().gen()])**c
+
+    def unit_gens(self, c):
+        r"""
+        A list of generators `x_1, \dots, x_d` of the abelian group `F^\times /
+        (1 + \mathfrak{p}^c)^\times`, where `c` is the given level, satisfying
+        no relations other than `x_i^{n_i} = 1` for each `i` (where the
+        integers `n_i` are returned by :meth:`exponents`). We adopt the
+        convention that the final generator `x_d` is a uniformiser. 
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupRamifiedQuadratic
+            sage: G = SmoothCharacterGroupRamifiedQuadratic(5, 0, QQ)
+            sage: G.unit_gens(0)
+            [s]
+            sage: G.unit_gens(1)
+            [2, s]
+            sage: G.unit_gens(8)
+            [2, s + 1, s]
+        """
+        d = ceil(ZZ(c) / 2)
+        p = self.prime()
+        K,s = self.number_field().objgen()
+        zpgens = [K(ZZ(x)) for x in Zmod(p**d).unit_gens()]
+        if c == 0:
+            return [s]
+        if c == 1:
+            return zpgens + [s]
+        elif p > 3 or self._unif_sqr == 3 or c <= 3:
+            return zpgens + [1 + s, s]
+        else: 
+            # Awkward case: K = Q_3(sqrt(-3)). Here the exponential map doesn't
+            # converge on 1 + P, and the quotient (O_K*) / (Zp*) isn't
+            # topologically cyclic. I don't know an explicit set of good
+            # generators here, so we let Pari do the work and put up with the
+            # rather arbitrary (nondeterministic?) results.
+            return list(self.ideal(c).idealstar(2).gens()) + [s]
+    
+    def exponents(self, c):
+        r"""
+        Return the orders of the independent generators of the unit group
+        returned by :meth:`~unit_gens`.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupRamifiedQuadratic
+            sage: G = SmoothCharacterGroupRamifiedQuadratic(5, 0, QQ)
+            sage: G.exponents(0)
+            [0]
+            sage: G.exponents(1)
+            [4, 0]
+            sage: G.exponents(8)
+            [500, 625, 0] 
+        """    
+        c = ZZ(c)
+        d = ceil(c / 2)
+        p = self.prime()
+        if c == 0:
+            return [0]
+        elif c == 1:
+            return [p - 1, 0]
+        elif p > 3 or self._unif_sqr == 3 or c <= 3:
+            return [p**(d-1)*(p - 1), p**ceil((c - 1)/2), 0]
+        else:
+            # awkward case, see above
+            return self.ideal(c).idealstar(2).invariants() + [0]
+
+    def subgroup_gens(self, level):
+        r"""
+        A set of elements of `(\mathcal{O}_F / \mathfrak{p}^c)^\times`
+        generating the kernel of the reduction map to `(\mathcal{O}_F /
+        \mathfrak{p}^{c-1})^\times`.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupRamifiedQuadratic
+            sage: G = SmoothCharacterGroupRamifiedQuadratic(3, 1, QQ)
+            sage: G.subgroup_gens(2)
+            [s + 1]
+        """
+        if level == 0: 
+            raise ValueError
+        elif level == 1:
+            return self.unit_gens(level)[:-1]
+        else:
+            return [1 + self.number_field().gen()**(level - 1)]
+
+    def discrete_log(self, level, x):
+        r"""
+        Solve the discrete log problem in the unit group.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupRamifiedQuadratic
+            sage: G = SmoothCharacterGroupRamifiedQuadratic(3, 1, QQ)
+            sage: s = G.number_field().gen()
+            sage: G.discrete_log(4, 3 + 2*s)
+            [5, 2, 1, 1]
+            sage: gs = G.unit_gens(4); gs[0]^5 * gs[1]^2 * gs[2] * gs[3] - (3 + 2*s) in G.ideal(4)
+            True
+        """
+        x = self.number_field().coerce(x)
+        if x == 0: raise ValueError, "cannot evaluate at zero"
+        n1 = x.valuation(self.ideal(1))
+        x1 = x / self.number_field().gen()**n1
+        if level == 0:
+            return [n1]
+        else:
+            return self.ideal(level).ideallog(x1, self.unit_gens(level)[:-1]) + [n1]
diff --git a/sage/modular/local_comp/type_space.py b/sage/modular/local_comp/type_space.py
new file mode 100644
--- /dev/null
+++ b/sage/modular/local_comp/type_space.py
@@ -0,0 +1,704 @@
+r"""
+Type spaces of newforms
+
+Let `f` be a new modular eigenform of level `\Gamma_1(N)`, and `p` a prime
+dividing `N`, with `N = Mp^r` (`M` coprime to `p`). Suppose the power of `p`
+dividing the conductor of the character of `f` is `p^c` (so `c \le r`).
+
+Then there is an integer `u`, which is `\operatorname{min}([r/2], r-c)`, such
+that any twist of `f` by a character mod `p^u` also has level `N`. The *type
+space* of `f` is the span of the modular eigensymbols corresponding to all of
+these twists, which lie in a space of modular symbols for a suitable `\Gamma_H`
+subgroup. This space is the key to computing the isomorphism class of the local
+component of the newform at `p`.
+
+"""
+
+import operator
+from sage.misc.misc import verbose, cputime
+from sage.modular.arithgroup.all import GammaH
+from sage.modular.modform.element import Newform
+from sage.modular.modform.constructor import ModularForms
+from sage.modular.modsym.modsym import ModularSymbols
+from sage.rings.all import ZZ, Zmod, QQ
+from sage.rings.fast_arith import prime_range
+from sage.rings.arith import crt
+from sage.structure.sage_object import SageObject
+from sage.groups.matrix_gps.special_linear import SL
+from sage.matrix.constructor import matrix
+from sage.misc.cachefunc import cached_method, cached_function
+
+from liftings import lift_gen_to_gamma1, lift_ramified
+
+@cached_function
+def example_type_space(example_no = 0):
+    r"""
+    Quickly return an example of a type space. Used mainly to speed up
+    doctesting.
+
+    EXAMPLE::
+
+        sage: from sage.modular.local_comp.type_space import example_type_space
+        sage: example_type_space()
+        6-dimensional type space at prime 7 of form q + q^2 + (-1/2*a1 + 1/2)*q^3 + q^4 + (a1 - 1)*q^5 + O(q^6)
+    """
+    from sage.modular.modform.constructor import Newform as Newform_constructor
+    if example_no == 0:
+        # a fairly generic example
+        return TypeSpace(Newform_constructor('98b', names='a'), 7)
+    elif example_no == 1:
+        # a non-minimal example
+        return TypeSpace(Newform_constructor('98a', names='a'), 7)
+    elif example_no == 2:
+        # a smaller example with QQ coefficients
+        return TypeSpace(Newform_constructor('50a'), 5)
+    elif example_no == 3:
+        # a ramified (odd p-power level) case
+        return TypeSpace(Newform_constructor('27a'), 3)
+
+def find_in_space(f, A, base_extend=False):
+    r"""
+    Given a Newform object `f`, and a space `A` of modular symbols of the same
+    weight and level, find the subspace of `A` which corresponds to the Hecke
+    eigenvalues of `f`. 
+
+    If ``base_extend = True``, this will return a 2-dimensional space generated
+    by the plus and minus eigensymbols of `f`. If ``base_extend = False`` it
+    will return a larger space spanned by the eigensymbols of `f` and its
+    Galois conjugates. 
+    
+    (NB: "Galois conjugates" needs to be interpreted carefully -- see the last
+    example below.)
+
+    `A` should be an ambient space (because non-ambient spaces don't implement
+    ``base_extend``).
+
+    EXAMPLES::
+
+        sage: from sage.modular.local_comp.type_space import find_in_space
+
+    Easy case (`f` has rational coefficients)::
+
+        sage: f = Newform('99a'); f
+        q - q^2 - q^4 - 4*q^5 + O(q^6)
+        sage: A = ModularSymbols(GammaH(99, [13]))
+        sage: find_in_space(f, A)
+        Modular Symbols subspace of dimension 2 of Modular Symbols space of dimension 25 for Congruence Subgroup Gamma_H(99) with H generated by [13] of weight 2 with sign 0 and over Rational Field
+
+    Harder case::
+
+        sage: f = Newforms(23, names='a')[0]
+        sage: A = ModularSymbols(Gamma1(23))
+        sage: find_in_space(f, A, base_extend=True)
+        Modular Symbols subspace of dimension 2 of Modular Symbols space of dimension 45 for Gamma_1(23) of weight 2 with sign 0 and over Number Field in a0 with defining polynomial x^2 + x - 1
+        sage: find_in_space(f, A, base_extend=False)
+        Modular Symbols subspace of dimension 4 of Modular Symbols space of dimension 45 for Gamma_1(23) of weight 2 with sign 0 and over Rational Field
+
+    An example with character, indicating the rather subtle behaviour of
+    ``base_extend``::
+
+        sage: chi = DirichletGroup(5).0
+        sage: f = Newforms(chi, 7, names='c')[0]; f
+        q + c0*q^2 + (zeta4*c0 - 5*zeta4 + 5)*q^3 + ((-5*zeta4 - 5)*c0 + 24*zeta4)*q^4 + ((10*zeta4 - 5)*c0 - 40*zeta4 - 55)*q^5 + O(q^6)
+        sage: find_in_space(f, ModularSymbols(Gamma1(5), 7), base_extend=True)
+        Modular Symbols subspace of dimension 2 of Modular Symbols space of dimension 12 for Gamma_1(5) of weight 7 with sign 0 and over Number Field in c0 with defining polynomial x^2 + (5*zeta4 + 5)*x - 88*zeta4 over its base field
+        sage: find_in_space(f, ModularSymbols(Gamma1(5), 7), base_extend=False)
+        Modular Symbols subspace of dimension 4 of Modular Symbols space of dimension 12 for Gamma_1(5) of weight 7 with sign 0 and over Cyclotomic Field of order 4 and degree 2
+
+    Note that the base ring in the second example is `\QQ(\zeta_4)` (the base
+    ring of the character of `f`), *not* `\QQ`.
+    """
+    if not A.weight() == f.weight():
+        raise ValueError( "Weight of space does not match weight of form" )
+    if not A.level() == f.level():
+        raise ValueError( "Level of space does not match level of form" )
+    
+    if base_extend:
+        D = A.base_extend(f.hecke_eigenvalue_field())
+    else:
+        M = f.modular_symbols(sign=1)
+        D = A.base_extend(M.base_ring())
+    
+    expected_dimension = 2 if base_extend else 2*M.dimension()
+
+    for p in prime_range(1 + A.sturm_bound()):
+        h = D.hecke_operator(p)
+        if base_extend:
+            hh = h - f[p]
+        else:
+            f = M.hecke_polynomial(p)
+            hh = f(h)
+        DD = hh.kernel()
+        if DD.dimension() < D.dimension(): 
+            D = DD
+        
+        if D.dimension() <= expected_dimension:
+            break 
+
+    if D.dimension() != expected_dimension:
+        raise ArithmeticError( "Error in find_in_space: "
+            + "got dimension %s (should be %s)" % (D.dimension(), expected_dimension) )
+    
+    return D
+
+class TypeSpace(SageObject):
+    r"""
+    The modular symbol type space associated to a newform, at a prime dividing
+    the level.
+    """
+    #################################################
+    # Basic initialisation and data-access functions
+    #################################################
+    
+    def __init__(self, f, p, base_extend=True):
+        r"""
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: example_type_space() # indirect doctest
+            6-dimensional type space at prime 7 of form q + q^2 + (-1/2*a1 + 1/2)*q^3 + q^4 + (a1 - 1)*q^5 + O(q^6)
+        """
+        self._p = p
+        self._f = f
+        if f.level() % p:
+            raise ValueError( "p must divide level" )
+        
+        amb = ModularSymbols(self.group(), f.weight())
+        self.e_space = find_in_space(f, amb, base_extend=base_extend).sign_submodule(1)
+        R = self.e_space.base_ring()
+        mat = amb._action_on_modular_symbols([p**self.u(), 1, 0, p**self.u()])
+        V = amb.free_module().base_extend(R)
+        bvecs = []
+        for v in self.e_space.free_module().basis():
+            bvecs += mat.maxspin(v)
+        T = V.submodule(bvecs)
+        self._unipmat = mat.change_ring(R).restrict(T).transpose() / ZZ(p ** (self.u() * (f.weight() - 2)))
+        self.t_space = amb.base_extend(R).submodule(T, check=False)
+
+    def _repr_(self):
+        r"""
+        String representation of self.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: example_type_space()._repr_()
+            '6-dimensional type space at prime 7 of form q + q^2 + (-1/2*a1 + 1/2)*q^3 + q^4 + (a1 - 1)*q^5 + O(q^6)'
+        """
+        return "%s-dimensional type space at prime %s of form %s" % (self.t_space.rank(), self.prime(), self.form())
+    
+    def prime(self):
+        r"""
+        Return the prime `p`.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: example_type_space().prime()
+            7
+        """
+        return self._p
+
+    def form(self):
+        r"""
+        The newform of which this is the type space.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: example_type_space().form()
+            q + q^2 + (-1/2*a1 + 1/2)*q^3 + q^4 + (a1 - 1)*q^5 + O(q^6)
+        """
+        return self._f
+
+    def conductor(self):
+        r"""
+        Exponent of `p` dividing the level of the form.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: example_type_space().conductor()
+            2
+        """
+        return self.form().level().valuation(self.prime())
+
+    def character_conductor(self):
+        r"""
+        Exponent of `p` dividing the conductor of the character of the form.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: example_type_space().character_conductor()
+            0
+        """
+        return ZZ(self.form().character().conductor()).valuation(self.prime())
+
+    def u(self):
+        r"""
+        Largest integer `u` such that level of `f_\chi` = level of `f` for all
+        Dirichlet characters `\chi` modulo `p^u`.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: example_type_space().u()
+            1
+            sage: from sage.modular.local_comp.type_space import TypeSpace
+            sage: f = Newforms(Gamma1(5), 5, names='a')[0]
+            sage: TypeSpace(f, 5).u()
+            0
+        """
+        return min(self.conductor() - self.character_conductor(), self.conductor() // 2)
+
+    def free_module(self):
+        r"""
+        Return the underlying vector space of this type space.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: example_type_space().free_module()
+            Vector space of dimension 6 over Number Field in a1 with defining polynomial ...
+        """
+        return self.t_space.nonembedded_free_module()
+
+    def eigensymbol_subspace(self):
+        r"""
+        Return the subspace of self corresponding to the plus eigensymbols of
+        `f` and its Galois conjugates (as a subspace of the vector space
+        returned by :meth:`~free_module`).
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: T = example_type_space(); T.eigensymbol_subspace()
+            Vector space of degree 6 and dimension 1 over Number Field in a1 with defining polynomial ...
+            Basis matrix:
+            [...]
+            sage: T.eigensymbol_subspace().is_submodule(T.free_module())
+            True
+        """
+        V = self.t_space.free_module()
+        vecs = [V.coordinate_vector(x) for x in self.e_space.free_module().basis()]
+        return vecs[0].parent().submodule(vecs)
+        
+    def tame_level(self):
+        r"""
+        The level away from `p`.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: example_type_space().tame_level()
+            2
+        """
+        return self.form().level() // self.prime() ** self.conductor()
+
+    def group(self):
+        r"""
+        Return a `\Gamma_H` group which is the level of all of the relevant
+        twists of `f`.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: example_type_space().group()
+            Congruence Subgroup Gamma_H(98) with H generated by [57]
+        """
+        p = self.prime()
+        r = self.conductor()
+        d = max(self.character_conductor(), r//2)
+        n = self.tame_level()
+        chi = self.form().character()
+        tame_H = [i for i in chi.kernel() if (i % p**r) == 1]
+        wild_H = [crt(1 + p**d, 1, p**r, n)]
+        return GammaH(n * p**r, tame_H + wild_H)
+
+    ###############################################################################
+    # Testing minimality: is this form a twist of a form of strictly smaller level?
+    ###############################################################################
+
+    @cached_method
+    def is_minimal(self):
+        r"""
+        Return True if there exists a newform `g` of level strictly smaller
+        than `N`, and a Dirichlet character `\chi` of `p`-power conductor, such
+        that `f = g \otimes \chi` where `f` is the form of which this is the
+        type space. To find such a form, use :meth:`~minimal_twist`.
+
+        The result is cached.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: example_type_space().is_minimal()
+            True
+            sage: example_type_space(1).is_minimal()
+            False
+        """
+        return self.t_space.is_submodule(self.t_space.ambient().new_submodule())
+
+    def minimal_twist(self):
+        r"""
+        Return a newform (not necessarily unique) which is a twist of the
+        original form `f` by a Dirichlet character of `p`-power conductor, and
+        which has minimal level among such twists of `f`. 
+
+        An error will be raised if `f` is already minimal.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import TypeSpace, example_type_space
+            sage: T = example_type_space(1)
+            sage: T.form().q_expansion(12)
+            q - q^2 + 2*q^3 + q^4 - 2*q^6 - q^8 + q^9 + O(q^12)
+            sage: g = T.minimal_twist()
+            sage: g.q_expansion(12)
+            q - q^2 - 2*q^3 + q^4 + 2*q^6 + q^7 - q^8 + q^9 + O(q^12)
+            sage: g.level()
+            14
+            sage: TypeSpace(g, 7).is_minimal()
+            True
+        """
+        if self.is_minimal():
+            raise ValueError( "Form is already minimal" )
+
+        NN = self.form().level()
+        V = self.t_space
+        A = V.ambient()
+        
+        while not V.is_submodule(A.new_submodule()):
+            NN = NN / self.prime()
+            D1 = A.degeneracy_map(NN, 1)
+            Dp = A.degeneracy_map(NN, self.prime())
+            A = D1.codomain()
+            vecs = [D1(v).element() for v in V.basis()] + [Dp(v).element() for v in V.basis()]
+            VV = A.free_module().submodule(vecs)
+            V = A.submodule(VV, check=False)
+         
+        D = V.decomposition()[0]
+        if len(D.star_eigenvalues()) == 1:
+            D._set_sign(D.star_eigenvalues()[0])
+        M = ModularForms(D.group(), D.weight())
+        ff = Newform(M, D, names='a')
+        return ff
+
+    #####################################
+    # The group action on the type space.
+    #####################################
+
+    def _rho_s(self, g):
+        r"""
+        Calculate the action of ``g`` on the type space, where ``g`` has determinant `1`.
+        For internal use; this gets called by :meth:`~rho`.
+        
+        EXAMPLES::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: T = example_type_space(2)
+            sage: T._rho_s([1,1,0,1])
+            [ 0  0  0 -1]
+            [ 0  0 -1  0]
+            [ 0  1 -2  1]
+            [ 1  0 -1  1]
+            sage: T._rho_s([0,-1,1,0])
+            [ 0  1 -2  1]
+            [ 0  0 -1  0]
+            [ 0 -1  0  0]
+            [ 1 -2  1  0]
+            sage: example_type_space(3)._rho_s([1,1,0,1])
+            [ 0  1]
+            [-1 -1]
+        """
+        if self.conductor() % 2 == 1:
+            return self._rho_ramified(g)
+            
+        else:
+            return self._rho_unramified(g)
+        
+    @cached_method
+    def _second_gen_unramified(self):
+        r"""
+        Calculate the action of the matrix [0, -1; 1, 0] on the type space,
+        in the unramified (even level) case.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: T = example_type_space(2)
+            sage: T._second_gen_unramified()
+            [ 0  1 -2  1]
+            [ 0  0 -1  0]
+            [ 0 -1  0  0]
+            [ 1 -2  1  0]
+            sage: T._second_gen_unramified()**4 == 1
+            True
+        """
+        f = self.prime() ** self.u()
+        g2 = lift_gen_to_gamma1(f, self.tame_level())
+        
+        g3 = [f * g2[0], g2[1], f**2 * g2[2], f*g2[3]]
+        A = self.t_space.ambient()
+        mm = A._action_on_modular_symbols(g3).restrict(self.t_space.free_module()).transpose()
+        m = mm / ZZ(f**(self.form().weight()-2))
+        return m
+        
+    def _rho_unramified(self, g):
+        r"""
+        Calculate the action of ``g`` on the type space, in the unramified (even
+        level) case. Uses the two standard generators, and a solution of the
+        word problem in `{\rm SL}_2(\ZZ / p^u \ZZ)`.
+
+        INPUT:
+
+        - ``g`` -- 4-tuple of integers (or more generally anything that can be
+          converted into an element of the matrix group `{\rm SL}_2(\ZZ / p^u
+          \ZZ)`).
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: T = example_type_space(2)
+            sage: T._rho_unramified([2,1,1,1])
+            [-1  1 -1  1]
+            [ 0  0  0  1]
+            [ 1 -1  0  1]
+            [ 1 -2  1  0]
+            sage: T._rho_unramified([1,-2,1,-1]) == T._rho_unramified([2,1,1,1]) * T._rho_unramified([0,-1,1,0])
+            True
+        """
+        f = self.prime() ** self.u()
+        G = SL(2, Zmod(f))
+        gg = G(g)
+        s = G([1,1,0,1])
+        t = G([0,-1,1,0])
+        S = self._unipmat
+        T = self._second_gen_unramified()
+        
+        w = gg.word_problem([s,t])
+        answer = S**0
+        for (x, n) in w:
+            if x == s:
+                answer = answer * S**n
+            elif x == t:
+                answer = answer * T**n
+        return answer
+
+    def _rho_ramified(self, g):
+         r"""
+         Calculate the action of a group element on the type space in the
+         ramified (odd conductor) case.
+
+         For internal use (called by :meth:`~rho`).
+
+         EXAMPLE::
+
+             sage: from sage.modular.local_comp.type_space import example_type_space
+             sage: T = example_type_space(3)
+             sage: T._rho_ramified([1,0,3,1])
+             [-1 -1]
+             [ 1  0]
+             sage: T._rho_ramified([1,3,0,1]) == 1
+             True
+         """
+         A = self.t_space.ambient()
+         g = map(ZZ, g)
+         p = self.prime()
+         assert g[2] % p == 0
+         gg = lift_ramified(g, p, self.u(), self.tame_level())
+         g3 = [p**self.u() * gg[0], gg[1], p**(2*self.u()) * gg[2], p**self.u() * gg[3]]
+         return A._action_on_modular_symbols(g3).restrict(self.t_space.free_module()).transpose() / ZZ(p**(self.u() * (self.form().weight()-2) ) )
+
+    def _group_gens(self):
+        r"""
+        Return a set of generators of the group `S(K_0) / S(K_u)` (which is
+        either `{\rm SL}_2(\ZZ / p^u \ZZ)` if the conductor is even, and a
+        quotient of an Iwahori subgroup if the conductor is odd).
+        
+        EXAMPLES::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: example_type_space()._group_gens()
+            [[1, 1, 0, 1], [0, -1, 1, 0]]
+            sage: example_type_space(3)._group_gens()
+            [[1, 1, 0, 1], [1, 0, 3, 1], [2, 0, 0, 5]]
+        """
+        if (self.conductor() % 2) == 0:
+            return [ [ZZ(1), ZZ(1), ZZ(0), ZZ(1)], [ZZ(0), ZZ(-1), ZZ(1), ZZ(0)] ]
+        else:
+            p = self.prime()
+            if p == 2:
+                return [ [ZZ(1), ZZ(1), ZZ(0), ZZ(1)], [ZZ(1), ZZ(0), ZZ(p), ZZ(1)] ]
+            else:
+                a = Zmod(p**(self.u() + 1))(ZZ(Zmod(p).unit_gens()[0]))
+                return [ [ZZ(1), ZZ(1), ZZ(0), ZZ(1)], [ZZ(1), ZZ(0), ZZ(p), ZZ(1)], 
+                         [ZZ(a), 0, 0, ZZ(~a)] ]
+
+    def _intertwining_basis(self, a):
+        r"""
+        Return a basis for the set of homomorphisms between
+        this representation and the same representation conjugated by 
+        [a,0; 0,1], where a is a generator of `(Z/p^uZ)^\times`. These are
+        the "candidates" for extending the rep to a `\mathrm{GL}_2`-rep.
+
+        Depending on the example, the hom-space has dimension either `1` or `2`.
+
+        EXAMPLES::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: example_type_space(2)._intertwining_basis(2)
+            [
+            [ 1 -2  1  0]
+            [ 1 -1  0  1]
+            [ 1  0 -1  1]
+            [ 0  1 -2  1]
+            ]
+            sage: example_type_space(3)._intertwining_basis(2)
+            [
+            [ 1  0]  [0 1]
+            [-1 -1], [1 0]
+            ]
+        """
+        if self.conductor() % 2:
+            f = self.prime() ** (self.u() + 1)
+        else: 
+            f = self.prime() ** self.u()
+        
+        # f is smallest p-power such that rho is trivial modulo f
+        ainv = (~Zmod(f)(a)).lift()
+        gens = self._group_gens()
+        gensconj = [[x[0], ainv*x[1], a*x[2], x[3]] for x in gens]
+        rgens = [self._rho_s(x) for x in gens]
+        rgensinv = map(operator.inv, rgens)
+        rgensconj = [self._rho_s(x) for x in gensconj]
+        
+        rows = []
+        MS = rgens[0].parent()
+        for m in MS.basis():
+            rows.append([])
+            for i in xrange(len(gens)):
+                rows[-1] += (m - rgensinv[i] * m * rgensconj[i]).list()
+        S = matrix(rows).left_kernel()
+        return [MS(u.list()) for u in S.gens()]
+        
+    def _discover_torus_action(self):
+        r"""
+        Calculate and store the data necessary to extend the action of `S(K_0)`
+        to `K_0`.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: example_type_space(2).rho([2,0,0,1]) # indirect doctest
+            [ 1 -2  1  0]
+            [ 1 -1  0  1]
+            [ 1  0 -1  1]
+            [ 0  1 -2  1]
+        """
+        f = self.prime() ** self.u()
+        if len(Zmod(f).unit_gens()) != 1:
+            raise NotImplementedError
+        a = ZZ(Zmod(f).unit_gens()[0])
+        
+        mats = self._intertwining_basis(a)
+        V = self.t_space.nonembedded_free_module()
+        v = self.eigensymbol_subspace().gen(0)
+        w = V.submodule_with_basis([m * v for m in mats]).coordinates(v) #v * self.e_space.diamond_eigenvalue(crt(a, 1, f, self.tame_level())))
+        self._a = a
+        self._amat = sum([mats[i] * w[i] for i in xrange(len(mats))])
+
+    def rho(self, g):
+        r"""
+        Calculate the action of the group element `g` on the type space.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: T = example_type_space(2)
+            sage: m = T.rho([2,0,0,1]); m
+            [ 1 -2  1  0]
+            [ 1 -1  0  1]
+            [ 1  0 -1  1]
+            [ 0  1 -2  1]
+            sage: v = T.eigensymbol_subspace().basis()[0]
+            sage: m * v == v
+            True
+
+        We test that it is a left action::
+
+            sage: T = example_type_space(0)
+            sage: a = [0,5,4,3]; b = [0,2,3,5]; ab = [1,4,2,2]
+            sage: T.rho(ab) == T.rho(a) * T.rho(b)
+            True
+
+        An odd level example::
+
+            sage: from sage.modular.local_comp.type_space import TypeSpace
+            sage: T = TypeSpace(Newform('54a'), 3)
+            sage: a = [0,1,3,0]; b = [2,1,0,1]; ab = [0,1,6,3]
+            sage: T.rho(ab) == T.rho(a) * T.rho(b)
+            True
+        """
+        if not self.is_minimal():
+            raise NotImplementedError( "Group action on non-minimal type space not implemented" )
+        
+        if self.u() == 0:
+           # silly special case: rep is principal series or special, so SL2
+           # action on type space is trivial
+           raise ValueError( "Representation is not supercuspidal" )
+
+        p = self.prime()
+        f = p**self.u()
+        g = map(ZZ, g)
+        d = (g[0]*g[3] - g[2]*g[1])
+        
+        # g is in S(K_0) (easy case)
+        if d % f == 1:
+            return self._rho_s(g)
+        
+        # g is in K_0, but not in S(K_0)
+
+        if d % p != 0:
+            try:
+                a = self._a
+            except:
+                self._discover_torus_action()
+                a = self._a
+            i = 0
+            while (d * a**i) % f != 1: 
+                i += 1
+                if i > f: raise ArithmeticError
+            return self._rho_s([a**i*g[0], g[1], a**i*g[2], g[3]]) * self._amat**(-i)
+        
+        # funny business
+
+        if (self.conductor() % 2 == 0):
+            if all([x.valuation(p) > 0 for x in g]):
+                eps = self.form().character()(crt(1, p, f, self.tame_level()))
+                return ~eps * self.rho([x // p for x in g])
+            else:
+                raise ArithmeticError( "g(={0}) not in K".format(g) )
+        
+        else:
+            m = matrix(ZZ, 2, g)
+            s = m.det().valuation(p)
+            mm = (matrix(QQ, 2, [0, -1, p, 0])**(-s) * m).change_ring(ZZ)
+            return self._unif_ramified()**s * self.rho(mm.list())
+
+    def _unif_ramified(self):
+        r"""
+        Return the action of [0,-1,p,0], in the ramified (odd p-power level)
+        case.
+
+        EXAMPLE::
+
+            sage: from sage.modular.local_comp.type_space import example_type_space
+            sage: T = example_type_space(3)
+            sage: T._unif_ramified()
+            [-1  0]
+            [ 0 -1]
+
+        """
+        return self.t_space.atkin_lehner_operator(self.prime()).matrix().transpose() * self.prime() ** (-1 + self.form().weight() // 2)
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -977,6 +977,7 @@
                      'sage.modular.quatalg',
                      'sage.modular.ssmod',
                      'sage.modular.overconvergent',
+                     'sage.modular.local_comp',
                      
                      'sage.monoids',
 
