# HG changeset patch
# User Jeroen Demeyer <jdemeyer@cage.ugent.be>
# Date 1282168698 -7200
# Node ID cd0623e7596fe89d62fe973fb649b2e50c3530fd
# Parent  cd88cc8db5eeb3d9da9c0378669c922fe9ca70f9
#9764: implement hashing for number field ideals that uses the HNF form instead of a string
#9764: make number field ideals *not* print in reduced form, use gens_two() if the discriminant is large
#9764: Use PARI's hash_GEN() for gen.__hash__

diff -r cd88cc8db5ee -r cd0623e7596f sage/libs/pari/gen.pyx
--- a/sage/libs/pari/gen.pyx	Thu Sep 16 02:43:11 2010 +0200
+++ b/sage/libs/pari/gen.pyx	Wed Aug 18 23:58:18 2010 +0200
@@ -416,8 +416,19 @@
         return P.new_gen_to_string(self.g)
 
     def __hash__(self):
-        _sig_on
-        return hash(P.new_gen_to_string(self.g))
+        """
+        Return the hash of self, computed using PARI's hash_GEN().
+        
+        TESTS::
+        
+            sage: type(pari('1 + 2.0*I').__hash__())
+            <type 'int'>
+        """
+        cdef long h
+        _sig_on
+        h = hash_GEN(self.g)
+        _sig_off
+        return h
 
     def _testclass(self):
         import test
diff -r cd88cc8db5ee -r cd0623e7596f sage/rings/number_field/number_field.py
--- a/sage/rings/number_field/number_field.py	Thu Sep 16 02:43:11 2010 +0200
+++ b/sage/rings/number_field/number_field.py	Wed Aug 18 23:58:18 2010 +0200
@@ -4285,7 +4285,7 @@
             sage: K.<a> = NumberField(x^4+3*x^2-17)
             sage: P = K.ideal(61).factor()[0][0]
             sage: K.residue_field(P)
-            Residue field in abar of Fractional ideal (-2*a^2 + 1)
+            Residue field in abar of Fractional ideal (61, a^2 + 30)
         
         ::
         
@@ -4299,7 +4299,7 @@
             sage: L.residue_field(P)
             Traceback (most recent call last):
             ...
-            ValueError: Fractional ideal (-2*a^2 + 1) is not an ideal of Number Field in b with defining polynomial x^2 + 5
+            ValueError: Fractional ideal (61, a^2 + 30) is not an ideal of Number Field in b with defining polynomial x^2 + 5
             sage: L.residue_field(2)
             Traceback (most recent call last):
             ...
@@ -7043,7 +7043,7 @@
 
             sage: C20 = CyclotomicField(20)
             sage: C20.different()
-            Fractional ideal (-4*zeta20^7 + 8*zeta20^5 - 12*zeta20^3 + 6*zeta20)
+            Fractional ideal (10, 2*zeta20^6 - 4*zeta20^4 - 4*zeta20^2 + 2)
             sage: C18 = CyclotomicField(18)
             sage: D = C18.different().norm()
             sage: D == C18.discriminant().abs()
diff -r cd88cc8db5ee -r cd0623e7596f sage/rings/number_field/number_field_ideal.py
--- a/sage/rings/number_field/number_field_ideal.py	Thu Sep 16 02:43:11 2010 +0200
+++ b/sage/rings/number_field/number_field_ideal.py	Wed Aug 18 23:58:18 2010 +0200
@@ -36,6 +36,8 @@
 #                  http://www.gnu.org/licenses/
 #*****************************************************************************
 
+SMALL_DISC = 1000000
+
 import operator
 
 import sage.misc.latex as latex
@@ -178,6 +180,21 @@
         if len(gens)==0:
             raise ValueError, "gens must have length at least 1 (zero ideal is not a fractional ideal)"
         Ideal_generic.__init__(self, field, gens, coerce)
+
+    def __hash__(self):
+        """
+        EXAMPLES::
+
+            sage: NumberField(x^2 + 1, 'a').ideal(7).__hash__()
+            -9223372036854775779                 # 64-bit
+            -2147483619                          # 32-bit
+        """
+        try: return self._hash
+        # At some point in the future (e.g., for relative extensions), we'll likely
+        # have to consider other hashes, like the following.
+        #except AttributeError: self._hash = hash(self.gens())
+        except AttributeError: self._hash = self.pari_hnf().__hash__()
+        return self._hash
         
     def _latex_(self):
         """
@@ -188,8 +205,27 @@
             '\\left(2, \\frac{1}{2} a - \\frac{1}{2}\\right)'
             sage: latex(K.ideal([2, 1/2*a - 1/2]))
             \left(2, \frac{1}{2} a - \frac{1}{2}\right)
+
+        The gens are reduced only if the norm of the discriminant of
+        the defining polynomial is at most
+        sage.rings.number_field.number_field_ideal.SMALL_DISC::
+
+            sage: K.<a> = NumberField(x^2 + 902384092834); K
+            Number Field in a with defining polynomial x^2 + 902384092834
+            sage: I = K.factor(19)[0][0]; I._latex_()
+            '\\left(19\\right)'
+
+        We can make the generators reduced by increasing SMALL_DISC.
+        We had better also set proof to False, or computing reduced
+        gens could take too long::
+        
+            sage: proof.number_field(False)
+            sage: sage.rings.number_field.number_field_ideal.SMALL_DISC = 10^20
+            sage: K.<a> = NumberField(x^4 + 3*x^2 - 17)
+            sage: K.ideal([17*a,17,17,17*a])._latex_()
+            '\\left(17\\right)'
         """
-        return '\\left(%s\\right)'%(", ".join(map(latex.latex, self.gens_reduced())))
+        return '\\left(%s\\right)'%(", ".join(map(latex.latex, self._gens_repr())))
        
     def __cmp__(self, other):
         """
@@ -363,7 +399,14 @@
 
     def _repr_short(self):
         """
-        Efficient string representation of this fraction ideal.
+        Compact string representation of this ideal.  When the norm of
+        the discriminant of the defining polynomial of the number field
+        is less than
+
+            sage.rings.number_field.number_field_ideal.SMALL_DISC
+            
+        then display reduced generators.  Otherwise display two
+        generators.
 
         EXAMPLES::
 
@@ -372,13 +415,63 @@
             sage: I = K.factor(17)[0][0]; I
             Fractional ideal (17, a^2 - 6)
             sage: I._repr_short()
-            '(17, a^2 - 6)'        
+            '(17, a^2 - 6)'
+
+        We use reduced gens, because the discriminant is small::
+        
+            sage: K.<a> = NumberField(x^2 + 17); K
+            Number Field in a with defining polynomial x^2 + 17
+            sage: I = K.factor(17)[0][0]; I
+            Fractional ideal (-a)
+
+        Here the discriminant is 'large', so the gens aren't reduced::
+
+            sage: sage.rings.number_field.number_field_ideal.SMALL_DISC
+            1000000
+            sage: K.<a> = NumberField(x^2 + 902384094); K
+            Number Field in a with defining polynomial x^2 + 902384094
+            sage: I = K.factor(19)[0][0]; I
+            Fractional ideal (19, a + 14)
+            sage: I.gens_reduced()                 # long time
+            (19, a + 14)
         """
-        #NOTE -- we will *have* to not reduce the gens soon, since this
-        # makes things insanely slow in general.
-        # When I fix this, I *have* to also change the _latex_ method.
-        return '(%s)'%(', '.join(map(str, self.gens_reduced())))
-#         return '(%s)'%(', '.join(map(str, self.gens())))
+        return '(%s)'%(', '.join(map(str, self._gens_repr())))
+
+    def _gens_repr(self):
+        """
+        Returns tuple of generators to be used for printing this number
+        field ideal. The gens are reduced only if the absolute value of
+        the norm of the discriminant of the defining polynomial is at
+        most sage.rings.number_field.number_field_ideal.SMALL_DISC.
+        
+        EXAMPLES::
+
+            sage: sage.rings.number_field.number_field_ideal.SMALL_DISC
+            1000000
+            sage: K.<a> = NumberField(x^4 + 3*x^2 - 17)
+            sage: K.discriminant()  # too big
+            -1612688
+            sage: I = K.ideal([17*a*(2*a-2),17*a*(2*a-3)]); I._gens_repr()
+            (289, 17*a)
+            sage: I.gens_reduced()
+            (17*a,)
+        """
+        # If the discriminant is small, it is easy to find nice gens.
+        # Otherwise it is potentially very hard.
+        try:
+            if abs(self.number_field().defining_polynomial().discriminant().norm()) <= SMALL_DISC:
+                return self.gens_reduced()
+        except TypeError:
+            # In some cases with relative extensions, computing the
+            # discriminant of the defining polynomial is not
+            # supported.
+            pass
+        # Return two generators unless the second one is zero
+        two_gens = self.gens_two()
+        if two_gens[1]:
+            return two_gens
+        else:
+            return (two_gens[0],)
 
     def _pari_(self):
         """
@@ -604,18 +697,21 @@
             sage: J = K.ideal([a+2, 9])
             sage: J.gens()
             (a + 2, 9)
-            sage: J.gens_reduced()
+            sage: J.gens_reduced()  # random sign
             (a + 2,)
             sage: K.ideal([a+2, 3]).gens_reduced()
             (3, a + 2)
 
         TESTS::
 
+            sage: len(J.gens_reduced()) == 1
+            True
+            
             sage: all(j.parent() is K for j in J.gens())
             True
             sage: all(j.parent() is K for j in J.gens_reduced())
             True
-
+            
             sage: K.<a> = NumberField(x^4 + 10*x^2 + 20)
             sage: J = K.prime_above(5)
             sage: J.is_principal()
@@ -1328,7 +1424,7 @@
             sage: K.<a> = CyclotomicField(11); K
             Cyclotomic Field of order 11 and degree 10
             sage: I = K.factor(31)[0][0]; I
-            Fractional ideal (-3*a^7 - 4*a^5 - 3*a^4 - 3*a^2 - 3*a - 3)
+            Fractional ideal (31, a^5 + 10*a^4 - a^3 + a^2 + 9*a - 1)
             sage: I.divides(I)
             True
             sage: I.divides(31)
@@ -1351,11 +1447,11 @@
             sage: I = K.ideal(19); I
             Fractional ideal (19)
             sage: F = I.factor(); F
-            (Fractional ideal (a^2 + 2*a + 2)) * (Fractional ideal (a^2 - 2*a + 2))
+            (Fractional ideal (19, 1/2*a^2 + a - 17/2)) * (Fractional ideal (19, 1/2*a^2 - a - 17/2))
             sage: type(F)
             <class 'sage.structure.factorization.Factorization'>
             sage: list(F)
-            [(Fractional ideal (a^2 + 2*a + 2), 1), (Fractional ideal (a^2 - 2*a + 2), 1)]
+            [(Fractional ideal (19, 1/2*a^2 + a - 17/2), 1), (Fractional ideal (19, 1/2*a^2 - a - 17/2), 1)]
             sage: F.prod()
             Fractional ideal (19)
         """
diff -r cd88cc8db5ee -r cd0623e7596f sage/rings/number_field/number_field_ideal_rel.py
--- a/sage/rings/number_field/number_field_ideal_rel.py	Thu Sep 16 02:43:11 2010 +0200
+++ b/sage/rings/number_field/number_field_ideal_rel.py	Wed Aug 18 23:58:18 2010 +0200
@@ -192,7 +192,8 @@
             sage: J.absolute_norm()
             22584817
             sage: J.absolute_ideal()
-            Fractional ideal (188/812911*a^5 - 1/812911*a^4 + 45120/812911*a^3 - 56/73901*a^2 + 3881638/812911*a + 50041/812911)
+            Fractional ideal (22584817, -4417/2438733*a^5 - 2867/1625822*a^4 - 1307249/4877466*a^3 - 112151/443406*a^2 - 22904674/2438733*a - 13720435234111/2438733)  # 32-bit
+            Fractional ideal (22584817, -1473/812911*a^5 + 8695/4877466*a^4 - 1308209/4877466*a^3 + 117415/443406*a^2 - 22963264/2438733*a - 13721081784272/2438733)   # 64-bit
             sage: J.absolute_ideal().norm()
             22584817
 
@@ -414,8 +415,7 @@
             sage: K.<c> = F.extension(Y^2 - (1 + a)*(a + b)*a*b)
             sage: I = K.ideal(3, c)
             sage: J = I.ideal_below(); J
-            Fractional ideal (b)   # 32-bit
-            Fractional ideal (-b)  # 64-bit
+            Fractional ideal (b)
             sage: J.number_field() == F
             True
         """
@@ -593,8 +593,8 @@
             sage: I.relative_ramification_index()
             2
             sage: I.ideal_below()
-            Fractional ideal (b)   # 32-bit
-            Fractional ideal (-b)  # 64-bit
+            Fractional ideal (-b)  # 32-bit
+            Fractional ideal (b)   # 64-bit
             sage: K.ideal(b) == I^2
             True
         """
@@ -770,7 +770,7 @@
         sage: K.<a> = NumberField(x^2+6)
         sage: L.<b> = K.extension(K['x'].gen()^4 + a)
         sage: I = L.ideal(b); I
-        Fractional ideal (b)
+        Fractional ideal (6, b)
         sage: is_NumberFieldFractionalIdeal_rel(I)
         True
         sage: N = I.relative_norm(); N
diff -r cd88cc8db5ee -r cd0623e7596f sage/rings/number_field/order.py
--- a/sage/rings/number_field/order.py	Thu Sep 16 02:43:11 2010 +0200
+++ b/sage/rings/number_field/order.py	Wed Aug 18 23:58:18 2010 +0200
@@ -673,7 +673,7 @@
             sage: P = K.ideal(61).factor()[0][0]
             sage: OK = K.maximal_order()
             sage: OK.residue_field(P)
-            Residue field in abar of Fractional ideal (-2*a^2 + 1)
+            Residue field in abar of Fractional ideal (61, a^2 + 30)
         """
         if self.is_maximal():
             return self.number_field().residue_field(prime, name, check)
diff -r cd88cc8db5ee -r cd0623e7596f sage/rings/number_field/small_primes_of_degree_one.py
--- a/sage/rings/number_field/small_primes_of_degree_one.py	Thu Sep 16 02:43:11 2010 +0200
+++ b/sage/rings/number_field/small_primes_of_degree_one.py	Wed Aug 18 23:58:18 2010 +0200
@@ -46,7 +46,7 @@
 EXAMPLES::
 
     sage: x = ZZ['x'].gen()
-    sage: F.<a> = NumberField(x^2 - 2, 'a')
+    sage: F.<a> = NumberField(x^2 - 2)
     sage: Ps = F.primes_of_degree_one_list(3)
     sage: Ps # random
     [Fractional ideal (2*a + 1), Fractional ideal (-3*a + 1), Fractional ideal (-a + 5)]
@@ -59,7 +59,7 @@
 
 The next two examples are for relative number fields.::
 
-    sage: L.<b> = F.extension(x^3 - a, 'b')
+    sage: L.<b> = F.extension(x^3 - a)
     sage: Ps = L.primes_of_degree_one_list(3)
     sage: Ps # random
     [Fractional ideal (17, b - 5), Fractional ideal (23, b - 4), Fractional ideal (31, b - 2)]
@@ -69,7 +69,7 @@
     True
     sage: all(P.residue_class_degree() == 1 for P in Ps)
     True
-    sage: M.<c> = NumberField(x^2 - x*b^2 + b, 'c')
+    sage: M.<c> = NumberField(x^2 - x*b^2 + b)
     sage: Ps = M.primes_of_degree_one_list(3)
     sage: Ps # random
     [Fractional ideal (17, c - 2), Fractional ideal (c - 1), Fractional ideal (41, c + 15)]
diff -r cd88cc8db5ee -r cd0623e7596f sage/rings/polynomial/polynomial_quotient_ring.py
--- a/sage/rings/polynomial/polynomial_quotient_ring.py	Thu Sep 16 02:43:11 2010 +0200
+++ b/sage/rings/polynomial/polynomial_quotient_ring.py	Wed Aug 18 23:58:18 2010 +0200
@@ -689,8 +689,8 @@
         `x^2 + 31` from 12 to 2, i.e. we lose a generator of order 6::
         
             sage: S.S_class_group([K.ideal(a)])
-            [((1/4*xbar^2 + 31/4, (-1/8*a + 1/8)*xbar^2 - 31/8*a + 31/8, 1/16*xbar^3 + 1/16*xbar^2 + 31/16*xbar + 31/16, -1/16*a*xbar^3 + (1/16*a + 1/8)*xbar^2 - 31/16*a*xbar + 31/16*a + 31/8), 6, 1/16*xbar^3 - 5/16*xbar^2 + 31/16*xbar - 139/16), ((-1/4*xbar^2 - 23/4, (1/8*a - 1/8)*xbar^2 + 23/8*a - 23/8, -1/16*xbar^3 - 1/16*xbar^2 - 23/16*xbar - 23/16, 1/16*a*xbar^3 + (-1/16*a - 1/8)*xbar^2 + 23/16*a*xbar - 23/16*a - 23/8), 2, -1/8*xbar^2 - 15/8)]     # 32-bit
-            [((1/4*xbar^2 + 31/4, (-1/8*a + 1/8)*xbar^2 - 31/8*a + 31/8, 1/16*xbar^3 + 1/16*xbar^2 + 31/16*xbar + 31/16, -1/16*a*xbar^3 + (1/16*a + 1/8)*xbar^2 - 31/16*a*xbar + 31/16*a + 31/8), 6, 1/16*xbar^3 - 5/16*xbar^2 + 31/16*xbar - 139/16), ((-1/4*xbar^2 - 23/4, (1/8*a - 1/8)*xbar^2 + 23/8*a - 23/8, -1/16*xbar^3 - 1/16*xbar^2 - 23/16*xbar - 23/16, 1/16*a*xbar^3 + (-1/16*a - 1/8)*xbar^2 + 23/16*a*xbar - 23/16*a - 23/8), 2, -1/8*xbar^2 - 15/8)] # 64-bit
+            [((1/4*xbar^2 + 31/4, (-1/8*a + 1/8)*xbar^2 - 31/8*a + 31/8, 1/16*xbar^3 + 1/16*xbar^2 + 31/16*xbar + 31/16, -1/16*a*xbar^3 + (1/16*a + 1/8)*xbar^2 - 31/16*a*xbar + 31/16*a + 31/8), 6, 1/16*xbar^3 - 5/16*xbar^2 + 31/16*xbar - 139/16), ((-1/4*xbar^2 - 23/4, (1/8*a - 1/8)*xbar^2 + 23/8*a - 23/8, -1/16*xbar^3 - 1/16*xbar^2 - 23/16*xbar - 23/16, 1/16*a*xbar^3 + (-1/16*a - 1/8)*xbar^2 + 23/16*a*xbar - 23/16*a - 23/8), 2, -1/8*xbar^2 - 15/8)]  # 32-bit
+            [((1/4*xbar^2 + 31/4, (-1/8*a + 1/8)*xbar^2 - 31/8*a + 31/8, 1/16*xbar^3 + 1/16*xbar^2 + 31/16*xbar + 31/16, -1/16*a*xbar^3 + (1/16*a + 1/8)*xbar^2 - 31/16*a*xbar + 31/16*a + 31/8), 6, -1/16*xbar^3 + 1/16*xbar^2 - 31/16*xbar + 47/16), ((-1/4*xbar^2 - 23/4, (1/8*a - 1/8)*xbar^2 + 23/8*a - 23/8, -1/16*xbar^3 - 1/16*xbar^2 - 23/16*xbar - 23/16, 1/16*a*xbar^3 + (-1/16*a - 1/8)*xbar^2 + 23/16*a*xbar - 23/16*a - 23/8), 2, -1/8*xbar^2 - 15/8)]  # 64-bit
 
         Note that all the returned values live where we expect them to::
         
diff -r cd88cc8db5ee -r cd0623e7596f sage/rings/residue_field.pyx
--- a/sage/rings/residue_field.pyx	Thu Sep 16 02:43:11 2010 +0200
+++ b/sage/rings/residue_field.pyx	Wed Aug 18 23:58:18 2010 +0200
@@ -172,7 +172,7 @@
     An example where the residue class field is large but of degree 1::
     
         sage: K.<a> = NumberField(x^3-875); P = K.ideal(2007).factor()[0][0]; k = K.residue_field(P); k
-        Residue field of Fractional ideal (-2/25*a^2 - 2/5*a - 3)
+        Residue field of Fractional ideal (223, 1/5*a + 11)
         sage: k(a)
         168
         sage: k(a)^3 - 875
@@ -468,8 +468,7 @@
         
             sage: K.<a> = NumberField(x^3-11)
             sage: F = K.ideal(37).factor(); F
-            (Fractional ideal (37, a + 12)) * (Fractional ideal (2*a - 5)) * (Fractional ideal (37, a + 9))   # 32-bit
-            (Fractional ideal (37, a + 12)) * (Fractional ideal (-2*a + 5)) * (Fractional ideal (37, a + 9))  # 64-bit
+            (Fractional ideal (37, a + 12)) * (Fractional ideal (-2*a + 5)) * (Fractional ideal (37, a + 9))
             sage: k = K.residue_field(F[0][0])
             sage: l = K.residue_field(F[1][0])
             sage: k == l
