Exemplo n.º 1
0
def number_field_elements_from_algebraics(elts, name='a'):
    r"""
    The native Sage function ``number_field_elements_from_algebraics`` currently
    returns number field *without* embedding. This function return field with
    embedding!

    EXAMPLES::

        sage: from flatsurf.geometry.subfield import number_field_elements_from_algebraics
        sage: z = QQbar.zeta(5)
        sage: c = z.real()
        sage: s = z.imag()
        sage: number_field_elements_from_algebraics((c,s))
        (Number Field in a with defining polynomial y^4 - 5*y^2 + 5 with a = 1.902113032590308?,
         [1/2*a^2 - 3/2, 1/2*a])

         sage: number_field_elements_from_algebraics([AA(1), AA(2/3)])
         (Rational Field, [1, 2/3])
    """
    # case when all elements are rationals
    if all(x in QQ for x in elts):
        return QQ, [QQ(x) for x in elts]

    # general case
    from sage.rings.qqbar import number_field_elements_from_algebraics
    field, elts, phi = number_field_elements_from_algebraics(elts,
                                                             minimal=True)

    polys = [x.polynomial() for x in elts]
    K = NumberField(field.polynomial(), name, embedding=AA(phi(field.gen())))
    gen = K.gen()

    return K, [x.polynomial()(gen) for x in elts]
 def __init__(self,lambda_squared=None, field=None):
     if lambda_squared==None:
         from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
         R=PolynomialRing(ZZ,'x')
         x = R.gen()
         field=NumberField(x**3-ZZ(5)*x**2+ZZ(4)*x-ZZ(1), 'r', embedding=AA(ZZ(4)))
         self._l=field.gen()
     else:
         if field is None:
             self._l=lambda_squared
             field=lambda_squared.parent()
         else:
             self._l=field(lambda_squared)
     Surface.__init__(self,field, ZZ.zero(), finite=False)
 def __init__(self, lambda_squared=None, field=None):
     if lambda_squared == None:
         from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
         R = PolynomialRing(ZZ, 'x')
         x = R.gen()
         field = NumberField(x**3 - ZZ(5) * x**2 + ZZ(4) * x - ZZ(1),
                             'r',
                             embedding=AA(ZZ(4)))
         self._l = field.gen()
     else:
         if field is None:
             self._l = lambda_squared
             field = lambda_squared.parent()
         else:
             self._l = field(lambda_squared)
     Surface.__init__(self, field, ZZ.zero(), finite=False)
Exemplo n.º 4
0
def is_cm_j_invariant(j, method='new'):
    """
    Return whether or not this is a CM `j`-invariant.

    INPUT:

    - ``j`` -- an element of a number field `K`

    OUTPUT:

    A pair (bool, (d,f)) which is either (False, None) if `j` is not a
    CM j-invariant or (True, (d,f)) if `j` is the `j`-invariant of the
    imaginary quadratic order of discriminant `D=df^2` where `d` is
    the associated fundamental discriminant and `f` the index.

    .. note::

       The current implementation makes use of the classification of
       all orders of class number up to 100, and hence will raise an
       error if `j` is an algebraic integer of degree greater than
       this.  It would be possible to implement a more general
       version, using the fact that `d` must be supported on the
       primes dividing the discriminant of the minimal polynomial of
       `j`.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant
        sage: is_cm_j_invariant(0)
        (True, (-3, 1))
        sage: is_cm_j_invariant(8000)
        (True, (-8, 1))

        sage: K.<a> = QuadraticField(5)
        sage: is_cm_j_invariant(282880*a + 632000)
        (True, (-20, 1))
        sage: K.<a> = NumberField(x^3 - 2)
        sage: is_cm_j_invariant(31710790944000*a^2 + 39953093016000*a + 50337742902000)
        (True, (-3, 6))

    TESTS::

        sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant
        sage: all([is_cm_j_invariant(j) == (True, (d,f)) for d,f,j in cm_j_invariants_and_orders(QQ)])
        True

    """
    # First we check that j is an algebraic number:

    from sage.rings.all import NumberFieldElement, NumberField
    if not isinstance(j, NumberFieldElement) and not j in QQ:
        raise NotImplementedError(
            "is_cm_j_invariant() is only implemented for number field elements"
        )

    # for j in ZZ we have a lookup-table:

    if j in ZZ:
        j = ZZ(j)
        table = dict([(jj, (d, f))
                      for d, f, jj in cm_j_invariants_and_orders(QQ)])
        if j in table:
            return True, table[j]
        return False, None

    # Otherwise if j is in Q then it is not integral so is not CM:

    if j in QQ:
        return False, None

    # Now j has degree at least 2.  If it is not integral so is not CM:

    if not j.is_integral():
        return False, None

    # Next we find its minimal polynomial and degree h, and if h is
    # less than the degree of j.parent() we recreate j as an element
    # of Q(j):

    jpol = PolynomialRing(QQ, 'x')([-j, 1
                                    ]) if j in QQ else j.absolute_minpoly()
    h = jpol.degree()

    # This will be used as a fall-back if we cannot determine the
    # result using local data.  For this to be necessary there would
    # have to be very few primes of degree 1 and norm under 1000,
    # since we only need to find one prime of degree 1, good
    # reduction for which a_P is nonzero.
    if method == 'old':
        if h > 100:
            raise NotImplementedError(
                "CM data only available for class numbers up to 100")
        for d, f in cm_orders(h):
            if jpol == hilbert_class_polynomial(d * f**2):
                return True, (d, f)
        return False, None

    # replace j by a clone whose parent is Q(j), if necessary:

    K = j.parent()
    if h < K.absolute_degree():
        K = NumberField(jpol, 'j')
        j = K.gen()

    # Construct an elliptic curve with j-invariant j, with
    # integral model:

    from sage.schemes.elliptic_curves.all import EllipticCurve
    E = EllipticCurve(j=j).integral_model()
    D = E.discriminant()
    prime_bound = 1000  # test primes of degree 1 up to this norm
    max_primes = 20  # test at most this many primes
    num_prime = 0
    cmd = 0
    cmf = 0

    # Test primes of good reduction.  If E has CM then for half the
    # primes P we will have a_P=0, and for all other prime P the CM
    # field is Q(sqrt(a_P^2-4N(P))).  Hence if these fields are
    # different for two primes then E does not have CM.  If they are
    # all equal for the primes tested, then we have a candidate CM
    # field.  Moreover the discriminant of the endomorphism ring
    # divides all the values a_P^2-4N(P), since that is the
    # discriminant of the order containing the Frobenius at P.  So we
    # end up with a finite number (usually one) of candidate
    # discriminants to test.  Each is tested by checking that its class
    # number is h, and if so then that j is a root of its Hilbert
    # class polynomial.  In practice non CM curves will be eliminated
    # by the local test at a small number of primes (probably just 2).

    for P in K.primes_of_degree_one_iter(prime_bound):
        if num_prime > max_primes:
            if cmd:  # we have a candidate CM field already
                break
            else:  # we need to try more primes
                max_primes *= 2
        if D.valuation(P) > 0:  # skip bad primes
            continue
        aP = E.reduction(P).trace_of_frobenius()
        if aP == 0:  # skip supersingular primes
            continue
        num_prime += 1
        DP = aP**2 - 4 * P.norm()
        dP = DP.squarefree_part()
        fP = ZZ(DP // dP).isqrt()
        if cmd == 0:  # first one, so store d and f
            cmd = dP
            cmf = fP
        elif cmd != dP:  # inconsistent with previous
            return False, None
        else:  # consistent d, so update f
            cmf = cmf.gcd(fP)

    if cmd == 0:  # no conclusion, we found no degree 1 primes, revert to old method
        return is_cm_j_invariant(j, method='old')

    # it looks like cm by disc cmd * f**2 where f divides cmf

    if cmd % 4 != 1:
        cmd = cmd * 4
        cmf = cmf // 2

    # Now we must check if h(cmd*f**2)==h for f|cmf; if so we check
    # whether j is a root of the associated Hilbert class polynomial.
    for f in cmf.divisors():  # only positive divisors
        d = cmd * f**2
        if h != d.class_number():
            continue
        pol = hilbert_class_polynomial(d)
        if pol(j) == 0:
            return True, (cmd, f)
    return False, None
Exemplo n.º 5
0
Arquivo: misc.py Projeto: merbst/psage
from sage.rings.all import NumberField, QQ

R = QQ['x']
x = R.gen()
F = NumberField(x * x - x - 1, 'a')
a = F.gen()
Exemplo n.º 6
0
def is_cm_j_invariant(j, method='new'):
    """
    Return whether or not this is a CM `j`-invariant.

    INPUT:

    - ``j`` -- an element of a number field `K`

    OUTPUT:

    A pair (bool, (d,f)) which is either (False, None) if `j` is not a
    CM j-invariant or (True, (d,f)) if `j` is the `j`-invariant of the
    imaginary quadratic order of discriminant `D=df^2` where `d` is
    the associated fundamental discriminant and `f` the index.

    .. note::

       The current implementation makes use of the classification of
       all orders of class number up to 100, and hence will raise an
       error if `j` is an algebraic integer of degree greater than
       this.  It would be possible to implement a more general
       version, using the fact that `d` must be supported on the
       primes dividing the discriminant of the minimal polynomial of
       `j`.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant
        sage: is_cm_j_invariant(0)
        (True, (-3, 1))
        sage: is_cm_j_invariant(8000)
        (True, (-8, 1))

        sage: K.<a> = QuadraticField(5)
        sage: is_cm_j_invariant(282880*a + 632000)
        (True, (-20, 1))
        sage: K.<a> = NumberField(x^3 - 2)
        sage: is_cm_j_invariant(31710790944000*a^2 + 39953093016000*a + 50337742902000)
        (True, (-3, 6))

    TESTS::

        sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant
        sage: all([is_cm_j_invariant(j) == (True, (d,f)) for d,f,j in cm_j_invariants_and_orders(QQ)])
        True

    """
    # First we check that j is an algebraic number:

    from sage.rings.all import NumberFieldElement, NumberField
    if not isinstance(j, NumberFieldElement) and not j in QQ:
        raise NotImplementedError("is_cm_j_invariant() is only implemented for number field elements")

    # for j in ZZ we have a lookup-table:

    if j in ZZ:
        j = ZZ(j)
        table = dict([(jj,(d,f)) for d,f,jj in cm_j_invariants_and_orders(QQ)])
        if j in table:
            return True, table[j]
        return False, None

    # Otherwise if j is in Q then it is not integral so is not CM:

    if j in QQ:
        return False, None

    # Now j has degree at least 2.  If it is not integral so is not CM:

    if not j.is_integral():
        return False, None

    # Next we find its minimal polynomial and degree h, and if h is
    # less than the degree of j.parent() we recreate j as an element
    # of Q(j):

    jpol = PolynomialRing(QQ,'x')([-j,1]) if j in QQ else j.absolute_minpoly()
    h = jpol.degree()

    # This will be used as a fall-back if we cannot determine the
    # result using local data.  For this to be necessary there would
    # have to be very few primes of degree 1 and norm under 1000,
    # since we only need to find one prime of degree 1, good
    # reduction for which a_P is nonzero.
    if method=='old':
        if h>100:
            raise NotImplementedError("CM data only available for class numbers up to 100")
        for d,f in cm_orders(h):
            if jpol == hilbert_class_polynomial(d*f**2):
                return True, (d,f)
        return False, None

    # replace j by a clone whose parent is Q(j), if necessary:

    K = j.parent()
    if h < K.absolute_degree():
        K = NumberField(jpol, 'j')
        j = K.gen()

    # Construct an elliptic curve with j-invariant j, with
    # integral model:

    from sage.schemes.elliptic_curves.all import EllipticCurve
    E = EllipticCurve(j=j).integral_model()
    D = E.discriminant()
    prime_bound = 1000 # test primes of degree 1 up to this norm
    max_primes =    20 # test at most this many primes
    num_prime = 0
    cmd = 0
    cmf = 0

    # Test primes of good reduction.  If E has CM then for half the
    # primes P we will have a_P=0, and for all other prime P the CM
    # field is Q(sqrt(a_P^2-4N(P))).  Hence if these fields are
    # different for two primes then E does not have CM.  If they are
    # all equal for the primes tested, then we have a candidate CM
    # field.  Moreover the discriminant of the endomorphism ring
    # divides all the values a_P^2-4N(P), since that is the
    # discriminant of the order containing the Frobenius at P.  So we
    # end up with a finite number (usually one) of candidate
    # discriminats to test.  Each is tested by checking that its class
    # number is h, and if so then that j is a root of its Hilbert
    # class polynomial.  In practice non CM curves will be eliminated
    # by the local test at a small number of primes (probably just 2).

    for P in K.primes_of_degree_one_iter(prime_bound):
        if num_prime > max_primes:
            if cmd: # we have a candidate CM field already
                break
            else:   # we need to try more primes
                max_primes *=2
        if D.valuation(P)>0: # skip bad primes
            continue
        aP = E.reduction(P).trace_of_frobenius()
        if aP == 0: # skip supersingular primes
            continue
        num_prime += 1
        DP = aP**2 - 4*P.norm()
        dP = DP.squarefree_part()
        fP = ZZ(DP//dP).isqrt()
        if cmd==0:      # first one, so store d and f
            cmd = dP
            cmf = fP
        elif cmd != dP: # inconsistent with previous
            return False, None
        else:           # consistent d, so update f
            cmf = cmf.gcd(fP)

    if cmd==0: # no conclusion, we found no degree 1 primes, revert to old method
        return is_cm_j_invariant(j, method='old')

    # it looks like cm by disc cmd * f**2 where f divides cmf

    if cmd%4!=1:
        cmd = cmd*4
        cmf = cmf//2

    # Now we must check if h(cmd*f**2)==h for f|cmf; if so we check
    # whether j is a root of the associated Hilbert class polynomial.
    for f in cmf.divisors(): # only positive divisors
        d = cmd*f**2
        if h != d.class_number():
            continue
        pol = hilbert_class_polynomial(d)
        if pol(j)==0:
            return True, (cmd,f)
    return False, None
    def arnoux_yoccoz(genus):
        r"""
        Construct the Arnoux-Yoccoz surface of genus 3 or greater.
        
        This presentation of the surface follows Section 2.3 of 
        Joshua P. Bowman's paper "The Complete Family of Arnoux-Yoccoz 
        Surfaces."
        
        EXAMPLES::

            sage: from flatsurf import *
            sage: s = translation_surfaces.arnoux_yoccoz(4)
            sage: TestSuite(s).run()
            sage: s.is_delaunay_decomposed()
            True
            sage: s = s.canonicalize()
            sage: field=s.base_ring()
            sage: a = field.gen()
            sage: from sage.matrix.constructor import Matrix
            sage: m = Matrix([[a,0],[0,~a]])
            sage: ss = m*s
            sage: ss = ss.canonicalize()
            sage: s.cmp_translation_surface(ss)==0
            True

        The Arnoux-Yoccoz pseudo-Anosov are known to have (minimal) invariant
        foliations with SAF=0::

            sage: S3 = translation_surfaces.arnoux_yoccoz(3)
            sage: Jxx, Jyy, Jxy = S3.j_invariant()
            sage: Jxx.is_zero() and Jyy.is_zero()
            True
            sage: Jxy
            [ 0  2  0]
            [ 2 -2  0]
            [ 0  0  2]

            sage: S4 = translation_surfaces.arnoux_yoccoz(4)
            sage: Jxx, Jyy, Jxy = S4.j_invariant()
            sage: Jxx.is_zero() and Jyy.is_zero()
            True
            sage: Jxy
            [ 0  2  0  0]
            [ 2 -2  0  0]
            [ 0  0  2  2]
            [ 0  0  2  0]
        """
        g=ZZ(genus)
        assert g>=3
        from sage.rings.polynomial.polynomial_ring import polygen
        x = polygen(AA)
        p=sum([x**i for i in xrange(1,g+1)])-1
        cp = AA.common_polynomial(p)
        alpha_AA = AA.polynomial_root(cp, RIF(1/2, 1))
        field=NumberField(alpha_AA.minpoly(),'alpha',embedding=alpha_AA)
        a=field.gen()
        from sage.modules.free_module import VectorSpace
        V=VectorSpace(field,2)
        p=[None for i in xrange(g+1)]
        q=[None for i in xrange(g+1)]
        p[0]=V(( (1-a**g)/2, a**2/(1-a) ))
        q[0]=V(( -a**g/2, a ))
        p[1]=V(( -(a**(g-1)+a**g)/2, (a-a**2+a**3)/(1-a) ))
        p[g]=V(( 1+(a-a**g)/2, (3*a-1-a**2)/(1-a) ))
        for i in xrange(2,g):
            p[i]=V(( (a-a**i)/(1-a) , a/(1-a) ))
        for i in xrange(1,g+1):
            q[i]=V(( (2*a-a**i-a**(i+1))/(2*(1-a)), (a-a**(g-i+2))/(1-a) ))
        from flatsurf.geometry.polygon import Polygons
        P=Polygons(field)
        s = Surface_list(field)
        T = [None] * (2*g+1)
        Tp = [None] * (2*g+1)
        from sage.matrix.constructor import Matrix
        m=Matrix([[1,0],[0,-1]])
        for i in xrange(1,g+1):
            # T_i is (P_0,Q_i,Q_{i-1})
            T[i]=s.add_polygon(P(edges=[ q[i]-p[0], q[i-1]-q[i], p[0]-q[i-1] ]))
            # T_{g+i} is (P_i,Q_{i-1},Q_{i})
            T[g+i]=s.add_polygon(P(edges=[ q[i-1]-p[i], q[i]-q[i-1], p[i]-q[i] ]))
            # T'_i is (P'_0,Q'_{i-1},Q'_i)
            Tp[i]=s.add_polygon(m*s.polygon(T[i]))
            # T'_{g+i} is (P'_i,Q'_i, Q'_{i-1})
            Tp[g+i]=s.add_polygon(m*s.polygon(T[g+i]))
        for i in xrange(1,g):
            s.change_edge_gluing(T[i],0,T[i+1],2)
            s.change_edge_gluing(Tp[i],2,Tp[i+1],0)
        for i in xrange(1,g+1):
            s.change_edge_gluing(T[i],1,T[g+i],1)
            s.change_edge_gluing(Tp[i],1,Tp[g+i],1)
        #P 0 Q 0 is paired with P' 0 Q' 0, ...
        s.change_edge_gluing(T[1],2,Tp[g],2)
        s.change_edge_gluing(Tp[1],0,T[g],0)
        # P1Q1 is paired with P'_g Q_{g-1}
        s.change_edge_gluing(T[g+1],2,Tp[2*g],2)
        s.change_edge_gluing(Tp[g+1],0,T[2*g],0)
        # P1Q0 is paired with P_{g-1} Q_{g-1}
        s.change_edge_gluing(T[g+1],0,T[2*g-1],2)
        s.change_edge_gluing(Tp[g+1],2,Tp[2*g-1],0)
        # PgQg is paired with Q1P2
        s.change_edge_gluing(T[2*g],2,T[g+2],0)
        s.change_edge_gluing(Tp[2*g],0,Tp[g+2],2)
        for i in xrange(2,g-1):
            # PiQi is paired with Q'_i P'_{i+1}
            s.change_edge_gluing(T[g+i],2,Tp[g+i+1],2)
            s.change_edge_gluing(Tp[g+i],0,T[g+i+1],0)
        s.set_immutable()
        return TranslationSurface(s)
Exemplo n.º 8
0
from sage.rings.all import NumberField, QQ

R = QQ['x']
x = R.gen()
F = NumberField(x*x - x- 1, 'a')
a = F.gen()
    def mcmullen_genus2_prototype(w, h, t, e, rel=0):
        r"""
        McMullen prototypes in the stratum H(2).

        These prototype appear at least in McMullen "Teichmüller curves in genus
        two: Discriminant and spin" (2004). The notation from that paper are
        quadruple ``(a, b, c, e)`` which translates in our notation as
        ``w = b``, ``h = c``, ``t = a`` (and ``e = e``).

        The associated discriminant is `D = e^2 + 4 wh`.

        If ``rel`` is a positive parameter (less than w-lambda) the surface belongs
        to the eigenform locus in H(1,1).

        EXAMPLES::

            sage: from flatsurf import translation_surfaces
            sage: from surface_dynamics import AbelianStratum

            sage: prototypes = {
            ....:      5: [(1,1,0,-1)],
            ....:      8: [(1,1,0,-2), (2,1,0,0)],
            ....:      9: [(2,1,0,-1)],
            ....:     12: [(1,2,0,-2), (2,1,0,-2), (3,1,0,0)],
            ....:     13: [(1,1,0,-3), (3,1,0,-1), (3,1,0,1)],
            ....:     16: [(3,1,0,-2), (4,1,0,0)],
            ....:     17: [(1,2,0,-3), (2,1,0,-3), (2,2,0,-1), (2,2,1,-1), (4,1,0,-1), (4,1,0,1)],
            ....:     20: [(1,1,0,-4), (2,2,1,-2), (4,1,0,-2), (4,1,0,2)],
            ....:     21: [(1,3,0,-3), (3,1,0,-3)],
            ....:     24: [(1,2,0,-4), (2,1,0,-4), (3,2,0,0)],
            ....:     25: [(2,2,0,-3), (2,2,1,-3), (3,2,0,-1), (4,1,0,-3)]}

            sage: for D in sorted(prototypes):
            ....:     for w,h,t,e in prototypes[D]:
            ....:          T = translation_surfaces.mcmullen_genus2_prototype(w,h,t,e)
            ....:          assert T.stratum() == AbelianStratum(2)
            ....:          assert (D.is_square() and T.base_ring() is QQ) or (T.base_ring().polynomial().discriminant() == D)

        An example with some relative homology::

            sage: U8 = translation_surfaces.mcmullen_genus2_prototype(2,1,0,0,1/4)    # discriminant 8
            sage: U12 = translation_surfaces.mcmullen_genus2_prototype(3,1,0,0,3/10)   # discriminant 12

            sage: U8.stratum()
            H_2(1^2)
            sage: U8.base_ring().polynomial().discriminant()
            8
            sage: U8.j_invariant()
            (
                      [4 0]
            (0), (0), [0 2]
            )

            sage: U12.stratum()
            H_2(1^2)
            sage: U12.base_ring().polynomial().discriminant()
            12
            sage: U12.j_invariant()
            (
                      [6 0]
            (0), (0), [0 2]
            )
        """
        w = ZZ(w)
        h = ZZ(h)
        t = ZZ(t)
        e = ZZ(e)
        g = w.gcd(h)
        gg = g.gcd(t).gcd(e)
        if w <= 0 or h <= 0 or t < 0 or t >= g or not g.gcd(t).gcd(
                e).is_one() or e + h >= w:
            raise ValueError("invalid parameters")

        x = polygen(QQ)
        poly = x**2 - e * x - w * h
        if poly.is_irreducible():
            emb = AA.polynomial_root(poly, RIF(0, w))
            K = NumberField(poly, 'l', embedding=emb)
            l = K.gen()
        else:
            K = QQ
            D = e**2 + 4 * w * h
            d = D.sqrt()
            l = (e + d) / 2
        rel = K(rel)

        # (lambda,lambda) square on top
        # twisted (w,0), (t,h)
        s = Surface_list(base_ring=K)
        if rel:
            if rel < 0 or rel > w - l:
                raise ValueError("invalid rel argument")
            s.add_polygon(
                polygons(vertices=[(0, 0), (l, 0), (l + rel, l), (rel, l)],
                         ring=K))
            s.add_polygon(
                polygons(vertices=[(0, 0), (rel, 0), (rel + l, 0), (w, 0),
                                   (w + t, h), (l + rel + t, h), (t + l, h),
                                   (t, h)],
                         ring=K))
            s.set_edge_pairing(0, 1, 0, 3)
            s.set_edge_pairing(0, 0, 1, 6)
            s.set_edge_pairing(0, 2, 1, 1)
            s.set_edge_pairing(1, 2, 1, 4)
            s.set_edge_pairing(1, 3, 1, 7)
            s.set_edge_pairing(1, 0, 1, 5)
        else:
            s.add_polygon(
                polygons(vertices=[(0, 0), (l, 0), (l, l), (0, l)], ring=K))
            s.add_polygon(
                polygons(vertices=[(0, 0), (l, 0), (w, 0), (w + t, h),
                                   (l + t, h), (t, h)],
                         ring=K))
            s.set_edge_pairing(0, 1, 0, 3)
            s.set_edge_pairing(0, 0, 1, 4)
            s.set_edge_pairing(0, 2, 1, 0)
            s.set_edge_pairing(1, 1, 1, 3)
            s.set_edge_pairing(1, 2, 1, 5)
        s.set_immutable()
        return TranslationSurface(s)
    def arnoux_yoccoz(genus):
        r"""
        Construct the Arnoux-Yoccoz surface of genus 3 or greater.

        This presentation of the surface follows Section 2.3 of
        Joshua P. Bowman's paper "The Complete Family of Arnoux-Yoccoz
        Surfaces."

        EXAMPLES::

            sage: from flatsurf import *
            sage: s = translation_surfaces.arnoux_yoccoz(4)
            sage: TestSuite(s).run()
            sage: s.is_delaunay_decomposed()
            True
            sage: s = s.canonicalize()
            sage: field=s.base_ring()
            sage: a = field.gen()
            sage: from sage.matrix.constructor import Matrix
            sage: m = Matrix([[a,0],[0,~a]])
            sage: ss = m*s
            sage: ss = ss.canonicalize()
            sage: s.cmp(ss) == 0
            True

        The Arnoux-Yoccoz pseudo-Anosov are known to have (minimal) invariant
        foliations with SAF=0::

            sage: S3 = translation_surfaces.arnoux_yoccoz(3)
            sage: Jxx, Jyy, Jxy = S3.j_invariant()
            sage: Jxx.is_zero() and Jyy.is_zero()
            True
            sage: Jxy
            [ 0  2  0]
            [ 2 -2  0]
            [ 0  0  2]

            sage: S4 = translation_surfaces.arnoux_yoccoz(4)
            sage: Jxx, Jyy, Jxy = S4.j_invariant()
            sage: Jxx.is_zero() and Jyy.is_zero()
            True
            sage: Jxy
            [ 0  2  0  0]
            [ 2 -2  0  0]
            [ 0  0  2  2]
            [ 0  0  2  0]
        """
        g = ZZ(genus)
        assert g >= 3
        x = polygen(AA)
        p = sum([x**i for i in range(1, g + 1)]) - 1
        cp = AA.common_polynomial(p)
        alpha_AA = AA.polynomial_root(cp, RIF(1 / 2, 1))
        field = NumberField(alpha_AA.minpoly(), 'alpha', embedding=alpha_AA)
        a = field.gen()
        V = VectorSpace(field, 2)
        p = [None for i in range(g + 1)]
        q = [None for i in range(g + 1)]
        p[0] = V(((1 - a**g) / 2, a**2 / (1 - a)))
        q[0] = V((-a**g / 2, a))
        p[1] = V((-(a**(g - 1) + a**g) / 2, (a - a**2 + a**3) / (1 - a)))
        p[g] = V((1 + (a - a**g) / 2, (3 * a - 1 - a**2) / (1 - a)))
        for i in range(2, g):
            p[i] = V(((a - a**i) / (1 - a), a / (1 - a)))
        for i in range(1, g + 1):
            q[i] = V(((2 * a - a**i - a**(i + 1)) / (2 * (1 - a)),
                      (a - a**(g - i + 2)) / (1 - a)))
        P = ConvexPolygons(field)
        s = Surface_list(field)
        T = [None] * (2 * g + 1)
        Tp = [None] * (2 * g + 1)
        from sage.matrix.constructor import Matrix
        m = Matrix([[1, 0], [0, -1]])
        for i in range(1, g + 1):
            # T_i is (P_0,Q_i,Q_{i-1})
            T[i] = s.add_polygon(
                P(edges=[q[i] - p[0], q[i - 1] - q[i], p[0] - q[i - 1]]))
            # T_{g+i} is (P_i,Q_{i-1},Q_{i})
            T[g + i] = s.add_polygon(
                P(edges=[q[i - 1] - p[i], q[i] - q[i - 1], p[i] - q[i]]))
            # T'_i is (P'_0,Q'_{i-1},Q'_i)
            Tp[i] = s.add_polygon(m * s.polygon(T[i]))
            # T'_{g+i} is (P'_i,Q'_i, Q'_{i-1})
            Tp[g + i] = s.add_polygon(m * s.polygon(T[g + i]))
        for i in range(1, g):
            s.change_edge_gluing(T[i], 0, T[i + 1], 2)
            s.change_edge_gluing(Tp[i], 2, Tp[i + 1], 0)
        for i in range(1, g + 1):
            s.change_edge_gluing(T[i], 1, T[g + i], 1)
            s.change_edge_gluing(Tp[i], 1, Tp[g + i], 1)
        #P 0 Q 0 is paired with P' 0 Q' 0, ...
        s.change_edge_gluing(T[1], 2, Tp[g], 2)
        s.change_edge_gluing(Tp[1], 0, T[g], 0)
        # P1Q1 is paired with P'_g Q_{g-1}
        s.change_edge_gluing(T[g + 1], 2, Tp[2 * g], 2)
        s.change_edge_gluing(Tp[g + 1], 0, T[2 * g], 0)
        # P1Q0 is paired with P_{g-1} Q_{g-1}
        s.change_edge_gluing(T[g + 1], 0, T[2 * g - 1], 2)
        s.change_edge_gluing(Tp[g + 1], 2, Tp[2 * g - 1], 0)
        # PgQg is paired with Q1P2
        s.change_edge_gluing(T[2 * g], 2, T[g + 2], 0)
        s.change_edge_gluing(Tp[2 * g], 0, Tp[g + 2], 2)
        for i in range(2, g - 1):
            # PiQi is paired with Q'_i P'_{i+1}
            s.change_edge_gluing(T[g + i], 2, Tp[g + i + 1], 2)
            s.change_edge_gluing(Tp[g + i], 0, T[g + i + 1], 0)
        s.set_immutable()
        return TranslationSurface(s)