Skip to content

Commit 0a5d0b7

Browse files
committed
Improve FiniteField_givaroElement
1 parent 4cdd703 commit 0a5d0b7

File tree

4 files changed

+50
-56
lines changed

4 files changed

+50
-56
lines changed

src/sage/combinat/designs/difference_family.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -802,8 +802,7 @@ def one_radical_difference_family(K, k):
802802
# instead of the complicated multiplicative group K^*/(±C) we use the
803803
# discrete logarithm to convert everything into the additive group Z/cZ
804804
c = m * (q-1) // e # cardinal of ±C
805-
from sage.groups.generic import discrete_log
806-
logA = [discrete_log(a,x) % c for a in A]
805+
logA = [a.log(x) % c for a in A]
807806

808807
# if two elements of A are equal modulo c then no tiling is possible
809808
if len(set(logA)) != m:

src/sage/rings/finite_rings/element_base.pyx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -723,7 +723,7 @@ cdef class FinitePolyExtElement(FiniteRingElement):
723723
sage: S(0).multiplicative_order()
724724
Traceback (most recent call last):
725725
...
726-
ArithmeticError: Multiplicative order of 0 not defined.
726+
ArithmeticError: multiplicative order of 0 not defined
727727
"""
728728
if self.is_zero():
729729
raise ArithmeticError("Multiplicative order of 0 not defined.")

src/sage/rings/finite_rings/element_givaro.pyx

Lines changed: 47 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ from cypari2.stack cimport clear_stack
6666

6767
from sage.structure.parent cimport Parent
6868
from sage.structure.element cimport Vector
69+
from sage.rings.fast_arith cimport arith_int
70+
cdef arith_int ai = arith_int()
6971

7072
from sage.interfaces.abc import GapElement
7173

@@ -837,6 +839,19 @@ cdef class FiniteField_givaro_iterator:
837839
cdef class FiniteField_givaroElement(FinitePolyExtElement):
838840
"""
839841
An element of a (Givaro) finite field.
842+
843+
Internal implementation detail: ``self.element`` is a ``cdef int`` member such that:
844+
845+
- if ``self.element == 0``, then ``self.is_zero()``,
846+
847+
- otherwise, ``self == g^self.element`` where ``g`` is a multiplicative generator.
848+
849+
In Givaro code, the type of ``element`` is known by the typename ``Rep``.
850+
851+
It is preferred to use the exposed interface of Givaro than to rely on this implementation detail.
852+
853+
The C function :func:`make_FiniteField_givaroElement` can be internally used to construct a
854+
:func:`FiniteField_givaroElement` object given ``int element``.
840855
"""
841856

842857
def __init__(FiniteField_givaroElement self, parent):
@@ -1036,7 +1051,7 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement):
10361051
sage: k(3).sqrt()
10371052
Traceback (most recent call last):
10381053
...
1039-
ValueError: must be a perfect square.
1054+
ValueError: must be a perfect square
10401055
10411056
TESTS::
10421057
@@ -1066,9 +1081,9 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement):
10661081
elif cache.objectptr.characteristic() == 2:
10671082
return make_FiniteField_givaroElement(cache, (cache.objectptr.cardinality() - 1 + self.element) / 2)
10681083
elif extend:
1069-
raise NotImplementedError # TODO: fix this once we have nested embeddings of finite fields
1084+
raise NotImplementedError # TODO: use RingExtension or GF(p^(2*e))
10701085
else:
1071-
raise ValueError("must be a perfect square.")
1086+
raise ValueError("must be a perfect square")
10721087

10731088
cpdef _add_(self, right):
10741089
"""
@@ -1227,6 +1242,7 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement):
12271242
12281243
ALGORITHM:
12291244
1245+
Makes use of the internal representation of Givaro objects.
12301246
Givaro objects are stored as integers `i` such that ``self`` `= a^i`,
12311247
where `a` is a generator of `K` (though not necessarily the one
12321248
returned by ``K.gens()``). Now it is trivial to compute
@@ -1244,23 +1260,24 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement):
12441260
exp = _exp
12451261

12461262
cdef Cache_givaro cache = self._cache
1263+
cdef GivaroGfq *objectptr = cache.objectptr
12471264

1248-
if (cache.objectptr).isOne(self.element):
1265+
if objectptr.isOne(self.element):
12491266
return self
12501267

12511268
elif exp == 0:
1252-
return make_FiniteField_givaroElement(cache, cache.objectptr.one)
1269+
return make_FiniteField_givaroElement(cache, objectptr.one)
12531270

1254-
elif (cache.objectptr).isZero(self.element):
1271+
elif objectptr.isZero(self.element):
12551272
if exp < 0:
12561273
raise ZeroDivisionError('division by zero in finite field')
1257-
return make_FiniteField_givaroElement(cache, cache.objectptr.zero)
1274+
return make_FiniteField_givaroElement(cache, objectptr.zero)
12581275

1259-
cdef int order = (cache.order_c() - 1)
1276+
cdef int order = cache.order_c() - 1
12601277
cdef int r = exp % order
12611278

12621279
if r == 0:
1263-
return make_FiniteField_givaroElement(cache, cache.objectptr.one)
1280+
return make_FiniteField_givaroElement(cache, objectptr.one)
12641281

12651282
cdef unsigned int r_unsigned
12661283
if r < 0:
@@ -1271,12 +1288,12 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement):
12711288
cdef unsigned int order_unsigned = <unsigned int>order
12721289
r = <int>(r_unsigned * elt_unsigned) % order_unsigned
12731290
if r == 0:
1274-
return make_FiniteField_givaroElement(cache, cache.objectptr.one)
1291+
return make_FiniteField_givaroElement(cache, objectptr.one)
12751292
return make_FiniteField_givaroElement(cache, r)
12761293

12771294
cpdef _richcmp_(left, right, int op):
12781295
"""
1279-
Comparison of finite field elements is correct or equality
1296+
Comparison of finite field elements is correct for equality
12801297
tests and somewhat random for ``<`` and ``>`` type of
12811298
comparisons. This implementation performs these tests by
12821299
comparing the underlying int representations.
@@ -1342,7 +1359,8 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement):
13421359
13431360
.. SEEALSO::
13441361
1345-
:meth:`sage.rings.finite_rings.element_base.FinitePolyExtElement.to_integer`
1362+
- :meth:`sage.rings.finite_rings.element_base.FinitePolyExtElement.to_integer`
1363+
- :meth:`_log_to_int`, identical to this method but returns a Sage :class:`~sage.rings.integer.Integer`.
13461364
13471365
EXAMPLES::
13481366
@@ -1372,7 +1390,7 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement):
13721390
...
13731391
TypeError: not in prime subfield
13741392
"""
1375-
cdef int a = self._cache.log_to_int(self.element)
1393+
cdef unsigned int a = self._cache.log_to_int(self.element)
13761394
if a < self._cache.objectptr.characteristic():
13771395
return Integer(a)
13781396
raise TypeError("not in prime subfield")
@@ -1386,6 +1404,10 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement):
13861404
`e = a_0 + a_1x + a_2x^2 + \cdots`, the int representation is
13871405
`a_0 + a_1 p + a_2 p^2 + \cdots`.
13881406
1407+
.. SEEALSO::
1408+
1409+
- :meth:`_integer_representation`, identical to this method but returns a Python ``int``.
1410+
13891411
EXAMPLES::
13901412
13911413
sage: k.<b> = GF(5^2); k
@@ -1411,35 +1433,25 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement):
14111433
- ``check`` -- boolean (default: ``False``): If set,
14121434
test whether the given ``order`` is correct.
14131435
1414-
.. WARNING::
1415-
1416-
TODO -- This is currently implemented by solving the discrete
1417-
log problem -- which shouldn't be needed because of how finite field
1418-
elements are represented.
1419-
14201436
EXAMPLES::
14211437
14221438
sage: k.<b> = GF(5^2); k
14231439
Finite Field in b of size 5^2
14241440
sage: a = b^7
14251441
sage: a.log(b)
14261442
7
1427-
1428-
TESTS:
1429-
1430-
An example for ``check=True``::
1431-
1432-
sage: F.<t> = GF(3^5, impl='givaro')
1433-
sage: t.log(t, 3^4, check=True)
1434-
Traceback (most recent call last):
1435-
...
1436-
ValueError: 81 is not a multiple of the order of the base
14371443
"""
1438-
b = self.parent()(base)
1444+
cdef FiniteField_givaroElement b = self.parent()(base)
1445+
if b.is_zero():
1446+
raise ValueError
14391447
if (order is not None) and check and not (b**order).is_one():
14401448
raise ValueError(f"{order} is not a multiple of the order of the base")
1441-
1442-
return sage.groups.generic.discrete_log(self, b, ord=order)
1449+
cdef int multiplicative_group_order = self._cache.order_c() - 1
1450+
cdef int gcd_b = ai.c_gcd_int(multiplicative_group_order, b.element)
1451+
if self.element % gcd_b != 0:
1452+
raise ValueError('no logarithm exists')
1453+
cdef int b_order = multiplicative_group_order / gcd_b
1454+
return self.element / gcd_b * <long long> ai.c_inverse_mod_int(b.element / gcd_b, b_order) % b_order
14431455

14441456
def _int_repr(FiniteField_givaroElement self):
14451457
r"""
@@ -1570,25 +1582,9 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement):
15701582
sage: (b^6).multiplicative_order()
15711583
4
15721584
"""
1573-
# TODO -- I'm sure this can be made vastly faster
1574-
# using how elements are represented as a power of the generator ??
1575-
1576-
if self._multiplicative_order is not None:
1577-
return self._multiplicative_order
1578-
else:
1579-
if self.is_zero():
1580-
raise ArithmeticError("Multiplicative order of 0 not defined.")
1581-
n = (self._cache).order_c() - 1
1582-
order = Integer(1)
1583-
for p, e in sage.arith.misc.factor(n):
1584-
# Determine the power of p that divides the order.
1585-
a = self**(n / (p**e))
1586-
while a != 1:
1587-
order = order * p
1588-
a = a**p
1589-
1590-
self._multiplicative_order = order
1591-
return order
1585+
if self.is_zero():
1586+
raise ArithmeticError("multiplicative order of 0 not defined")
1587+
return Integer((self._cache.order_c() - 1) / ai.c_gcd_int(self.element, self._cache.order_c() - 1))
15921588

15931589
def __copy__(self):
15941590
"""

src/sage/tests/books/judson-abstract-algebra/finite-sage.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,5 @@
102102
sage: (3*a^3 + 2*a^2 + a + 3).log(a^2 + 4*a + 4)
103103
Traceback (most recent call last):
104104
...
105-
ValueError: no discrete log of 3*a^3 + 2*a^2 + a + 3 found
106-
to base a^2 + 4*a + 4
105+
ValueError: no logarithm exists
107106
"""

0 commit comments

Comments
 (0)