# HG changeset patch
# User Simon King <simon.king@nuigalway.ie>
# Date 1278582840 -3600
# Node ID 7f2123c51909cc91236d1714df3ec71e9b7b1340
# Parent  5d639a38bab1c19b21df66a8f298ed9e41e839b0
#2420: Interface cache for parent structures; GAP interface for polynomials.

diff --git a/sage/rings/polynomial/multi_polynomial.pyx b/sage/rings/polynomial/multi_polynomial.pyx
--- a/sage/rings/polynomial/multi_polynomial.pyx
+++ b/sage/rings/polynomial/multi_polynomial.pyx
@@ -786,6 +786,75 @@
         P = P.change_ring(R)
         return P(self)
 
+    def _gap_(self, gap=None):
+        """
+        Return a representation of ``self`` in the GAP interface
+
+        INPUT:
+
+        ``gap`` -- (optional GAP instance) if not provided,
+                   sage.all.gap is used.
+
+        NOTE:
+
+        The result is not cached.
+
+        TESTS:
+
+        Multivariate polynomial over a cyclotomic field::
+
+            sage: F.<zeta> = CyclotomicField(8)
+            sage: P.<x,y> = F[]
+            sage: p = zeta + zeta^2*x + zeta^3*y + (1+zeta)*x*y
+            sage: gap(p)    # indirect doctest
+            (1+E(8))*x*y+E(4)*x+E(8)^3*y+E(8)
+
+        Multivariate polynomial over a number field::
+
+            sage: Q.<t> = QQ[]
+            sage: K.<tau> = NumberField(t^2+t+1)
+            sage: P.<x,y> = K[]
+            sage: p = tau + tau^2*x + tau^3*y + (1+tau)*x*y
+            sage: p
+            (tau + 1)*x*y + (-tau - 1)*x + y + (tau)
+            sage: gap(p)     # indirect doctest
+            (tau+1)*x*y+(-tau-1)*x+y+tau
+
+        Multivariate polynomial over a polynomial ring
+        over a number field::
+
+            sage: S.<z> = K[]
+            sage: P.<x,y> = S[]
+            sage: p = tau + tau^2*x*z + tau^3*y*z^2 + (1+tau)*x*y*z
+            sage: p
+            ((tau + 1)*z)*x*y + ((-tau - 1)*z)*x + z^2*y + tau
+            sage: gap(p)     # indirect doctest
+            ((tau+1)*z)*x*y+((-tau-1)*z)*x+z^2*y+tau
+
+        AUTHOR:
+
+        - Simon King (2010-07)
+        """
+        if gap is None:
+            from sage.all import gap
+        if self==0:
+            return gap(0)
+        r = gap(self.parent())  # this is cached
+        s = r.name()+'.%d^%d'
+        cdef dict D = self.dict()
+        cdef list L = []
+        n = self.parent().ngens()
+        for a,b in D.items():
+            m = '*'.join([s%(i+1,a[i]) for i in range(n) if a[i]])
+            if m:
+                L.append(gap(b)*gap(m))
+            else:
+                if n:
+                    L.append(gap(b)+gap('0*'+r.name()+'.1'))
+                else:
+                    L.append(gap(b))
+        return gap.List(L).Sum()
+
     def _magma_init_(self, magma):
         """
         Returns a Magma string representation of self valid in the
diff --git a/sage/rings/polynomial/multi_polynomial_ring_generic.pyx b/sage/rings/polynomial/multi_polynomial_ring_generic.pyx
--- a/sage/rings/polynomial/multi_polynomial_ring_generic.pyx
+++ b/sage/rings/polynomial/multi_polynomial_ring_generic.pyx
@@ -326,7 +326,60 @@
         s = 'PolynomialRing(%s,%s,%s)'%(Bref, self.ngens(), self.term_order().magma_str())
         return magma._with_names(s, self.variable_names())
 
+    def _gap_init_(self,gap=None):
+        """
+        Return a string that yields a representation of ``self`` in GAP.
+
+        INPUT:
+
+        ``gap`` -- (optional GAP instance) Interface to which the 
+                   string is addressed.
+
+        NOTE:
+
+        - If the optional argument ``gap`` is provided, the base ring
+          of ``self`` will be represented as ``gap(self.base_ring()).name()``.
+        - The result of applying the GAP interface to ``self`` is cached.
+
+        EXAMPLE::
+
+            sage: F = CyclotomicField(8)
+            sage: P.<x,y> = F[]
+            sage: gap(P)     # indirect doctest
+            PolynomialRing( CF(8), ["x", "y"] )
+            sage: gap(P) is gap(P)
+            True
+
+        AUTHOR:
+
+        - Simon King (2010-07)
+        """
+        L = ['"%s"'%t for t in self.variable_names()]
+        if gap is not None:
+            return 'PolynomialRing(%s,[%s])'%(gap(self.base_ring()).name(),','.join(L))
+        return 'PolynomialRing(%s,[%s])'%(self.base_ring()._gap_init_(),','.join(L))
+
     def is_finite(self):
+        """
+        Tell whether ``self`` is finite.
+
+        NOTE:
+
+        ``self`` is finite if and only if it has no variables
+        and the base ring is finite.
+
+        EXAMPLES::
+
+            sage: P = PolynomialRing(QQ,names=[])
+            sage: P.is_finite()
+            False
+            sage: P = PolynomialRing(GF(5),names=[])
+            sage: P.is_finite()
+            True
+            sage: P = PolynomialRing(GF(5),names=['x'])
+            sage: P.is_finite()
+            False
+        """
         if self.ngens() == 0:
             return self.base_ring().is_finite()
         return False
diff --git a/sage/rings/polynomial/polynomial_element.pyx b/sage/rings/polynomial/polynomial_element.pyx
--- a/sage/rings/polynomial/polynomial_element.pyx
+++ b/sage/rings/polynomial/polynomial_element.pyx
@@ -4013,25 +4013,30 @@
         v = ','.join([a._magma_init_(magma) for a in self.list()])
         return '%s![%s]'%(R.name(), v)
 
-    def _gap_init_(self):
-        return repr(self)
-
-    def _gap_(self, G):
-        """
+    def _gap_(self, gap=None):
+        """
+        Represent ``self`` in GAP.
+
+        INPUT:
+
+        ``gap`` -- optional GAP instance`.
+
+        NOTE:
+
+        The result is not cached.
+
         EXAMPLES::
         
             sage: R.<y> = ZZ[]
             sage: f = y^3 - 17*y + 5
-            sage: g = gap(f); g
+            sage: g = gap(f); g  # indirect doctest
             y^3-17*y+5
-            sage: f._gap_init_()
-            'y^3 - 17*y + 5'
             sage: R.<z> = ZZ[]
             sage: gap(R)
             PolynomialRing( Integers, ["z"] )
             sage: g
             y^3-17*y+5
-            sage: gap(z^2 + z)
+            sage: gap(z^2 + z)  # indirect doctest
             z^2+z
         
         We coerce a polynomial with coefficients in a finite field::
@@ -4044,12 +4049,41 @@
             [ y+Z(7)^0, y+Z(7)^0, y+Z(7)^5 ]
             sage: f.factor()
             (y + 5) * (y + 1)^2
-        """
-        if G is None:
-            import sage.interfaces.gap
-            G = sage.interfaces.gap.gap
-        self.parent()._gap_(G)
-        return G(self._gap_init_())
+
+        TEST:
+
+        A univariate polynomial over a multivariate polynomial
+        ring over a number field::
+
+            sage: Q.<t> = QQ[]
+            sage: K.<tau> = NumberField(t^2+t+1)
+            sage: P.<x,y> = K[]
+            sage: R.<z> = P[]
+            sage: p = tau + tau^2*x*z + tau^3*y*z^2 + (1+tau)*x*y*z
+            sage: p
+            y*z^2 + ((tau + 1)*x*y + (-tau - 1)*x)*z + (tau)
+            sage: gap(p)
+            y*z^2+((tau+1)*x*y+(-tau-1)*x)*z+tau
+
+        AUTHOR:
+
+        - Simon King (2010-07)
+        """
+        if gap is None:
+            from sage.all import gap
+        if self==0:
+            return gap(0)
+        r = gap(self.parent()) # this is cached!
+        s = r.name()+'.1'
+        cdef dict D = self.dict()
+        cdef list L = []
+        for a,b in D.items():
+            if a:
+                L.append(gap(b)*gap(s+'^%d'%a))
+            else:
+                # needed, since otherwise GAP can't add!
+                L.append(gap(b)+gap('0*'+s))
+        return gap.List(L).Sum() 
 
     ######################################################################
     
diff --git a/sage/rings/polynomial/polynomial_ring.py b/sage/rings/polynomial/polynomial_ring.py
--- a/sage/rings/polynomial/polynomial_ring.py
+++ b/sage/rings/polynomial/polynomial_ring.py
@@ -604,27 +604,60 @@
         s = 'PolynomialRing(%s)'%(Bref)
         return magma._with_names(s, self.variable_names())
 
-    def _gap_(self, G=None):
+#    def _gap_(self, G=None):
+#        """
+#        Used in converting this ring to the corresponding ring in GAP.
+#
+#        EXAMPLES::
+#        
+#            sage: R.<z> = ZZ[]
+#            sage: gap(R)
+#            PolynomialRing( Integers, ["z"] )
+#            sage: gap(z^2 + z)
+#            z^2+z
+#        """
+#        if G is None:
+#            import sage.interfaces.gap
+#            G = sage.interfaces.gap.gap
+#        R = G(self._gap_init_())
+#        v = self.variable_name()
+#        G.eval('%s := IndeterminatesOfPolynomialRing(%s)[1]'%(v, R.name()))
+#        return R
+
+    def _gap_init_(self,gap=None):
         """
-        Used in converting this ring to the corresponding ring in GAP.
-        
-        EXAMPLES::
-        
-            sage: R.<z> = ZZ[]
-            sage: gap(R)
-            PolynomialRing( Integers, ["z"] )
-            sage: gap(z^2 + z)
-            z^2+z
+        String for representing ``self`` in GAP.
+
+        INPUT:
+
+        ``gap`` -- (optional GAP instance) used for representing the base ring.
+
+        NOTE:
+
+        Interface representations of parent structures are cached.
+        Hence, if ``gap`` is provided, ``gap(self.base_ring()).name()``
+        is a short yet unique representation of the base ring.
+
+        TESTS:
+
+        A univariate polynomial ring over a multivariate polynomial
+        ring over a number field::
+
+            sage: Q.<t> = QQ[]
+            sage: K.<tau> = NumberField(t^2+t+1)
+            sage: P.<x,y> = K[]
+            sage: S.<z> = P[]
+            sage: gap(S)
+            PolynomialRing( PolynomialRing( <algebraic extension over the Rationals of degree 2>, ["x", "y"] ), ["z"] )
+            sage: gap(S) is gap(S)
+            True
+
+        AUTHOR:
+
+        - Simon King (2010-07)
         """
-        if G is None:
-            import sage.interfaces.gap
-            G = sage.interfaces.gap.gap
-        R = G(self._gap_init_())
-        v = self.variable_name()
-        G.eval('%s := IndeterminatesOfPolynomialRing(%s)[1]'%(v, R.name()))
-        return R
-
-    def _gap_init_(self):
+        if gap is not None:
+            return 'PolynomialRing(%s, ["%s"])'%(gap(self.base_ring()).name(), self.variable_name())
         return 'PolynomialRing(%s, ["%s"])'%(self.base_ring()._gap_init_(), self.variable_name())
 
     def _sage_input_(self, sib, coerced):
diff --git a/sage/structure/parent.pxd b/sage/structure/parent.pxd
--- a/sage/structure/parent.pxd
+++ b/sage/structure/parent.pxd
@@ -22,6 +22,7 @@
     cdef public _initial_action_list
     cdef public _initial_convert_list
     cdef readonly bint _coercions_used
+    cdef dict __interface_cache
     
     cpdef bint is_coercion_cached(self, domain)
     cpdef bint is_conversion_cached(self, domain)
diff --git a/sage/structure/parent.pyx b/sage/structure/parent.pyx
--- a/sage/structure/parent.pyx
+++ b/sage/structure/parent.pyx
@@ -1399,6 +1399,69 @@
         """
         return self.list()[int(n):int(m)]
 
+    def _get_interface_cache_(self, I):
+        """
+        Return a cached interface representation of ``self``, or ``None``.
+
+        INPUT:
+
+        ``I`` -- An instance of an interface.
+
+        TESTS::
+
+            sage: Q.<t> = QQ[]
+            sage: K.<tau> = NumberField(t^2+t+1)
+            sage: P.<x,y> = K[]
+            sage: gap(P)
+            PolynomialRing( <algebraic extension over the Rationals of degree
+            2>, ["x", "y"] )
+            sage: P._get_interface_cache_(gap)
+            PolynomialRing( <algebraic extension over the Rationals of degree
+            2>, ["x", "y"] )
+            sage: P._get_interface_cache_(singular)
+            sage: gap(P) is gap(P)    # indirect doctest
+            True
+
+        AUTHOR:
+
+        - Simon King (2010-07)
+        """
+        if self.__interface_cache is not None:
+            return self.__interface_cache.get(I)
+        return None
+
+    def _set_interface_cache_(self, I, X):
+        """
+        Store an interface representation of ``self`` in the cache.
+
+        INPUT:
+
+        - ``I`` -- an interface instance
+        - ``X`` -- the value to be stored
+
+        NOTE:
+
+        The value can be retrieved with :meth:`_get_interface_cache_`.
+
+        TESTS::
+
+            sage: Q.<t> = QQ[]
+            sage: K.<tau> = NumberField(t^2+t+1)
+            sage: P.<x,y> = K[]
+            sage: gap(P) is gap(P)
+            True
+            sage: P._set_interface_cache_(magma, 'foobar')
+            sage: P._get_interface_cache_(magma)
+            'foobar'
+
+        AUTHOR:
+
+        - Simon King (2010-07)
+        """
+        if self.__interface_cache is None:
+            self.__interface_cache = {}
+        self.__interface_cache[I] = X
+
     #################################################################################
     # Generators and Homomorphisms
     #################################################################################
diff --git a/sage/structure/sage_object.pyx b/sage/structure/sage_object.pyx
--- a/sage/structure/sage_object.pyx
+++ b/sage/structure/sage_object.pyx
@@ -414,7 +414,11 @@
         c = self._interface_is_cached_()
         if c:
             try:
-                X = self.__interface[I]
+                try:
+                    X = self.__interface[I]
+                except AttributeError:
+                    X = self._get_interface_cache_(I)
+                # X might be None, which is OK as we catch the AttributeError
                 X._check_valid()
                 return X
             except (AttributeError, TypeError):
@@ -441,7 +445,10 @@
             try:
                 self.__interface[I] = X
             except AttributeError:
-                pass
+                try: # Parent structures will have a cache!
+                    self._set_interface_cache_(I,X)
+                except AttributeError: # self is cdef and no Parent...
+                    pass
         return X
 
     def _interface_init_(self, I=None):
