Given a polynomial ring R
, one wants to be able to define
elements in its fraction field Q
.
Here, this is achieved by creating a class fastfrac
where an
element in the fraction field is stored as an object f
in this
class, with a numerator stored as f.top
and a denominator stored
as f.bot
(the numerator and denominator are the "top" and
"bottom" of the fraction).
The __init__
method of this class fastfrac
is built such that:
calling fastfrac(a, b)
, with a
and b
in R
,
will return a fraction representing a/b
, so it will have
top
equal to a
and bot
equal to b
;
this should also work if a
is already in Q
;
in that case, imagining that a = c/d
with c
, d
in R
,
the fraction f
equal to a/b
should be equal to (c/d)/b
,
that is, c/(d*b)
;
the implementation is split into two subcases:
if a
is in the class fastfrac, this means the top
of a/b
should be a.top
and its bot
should be a.bot * b
;
otherwise (maybe a
belongs to some other implementation
of the fraction field), we access the top and bottom of a
by numerator(a)
and denominator(b)
and then make sure
to transform them into elements of R
by doing
R(numerator(a))
and R(denominator(a))
; so f
has
top equal to R(numerator(a))
and bottom equal to
R(denominator(a)) * b
;
finally, one also wants to be able to call fastfrac(a)
instead of fastfrac(a, 1)
; so if fastfrac is called with
just one argument (playing the role of a
), an extra argument
equal to 1
(playing the role of b
) is inserted.
The if/elif/else clause in the __init__
method of the class fastfrac
corresponds to the first list item and the two subitems of the second
list item from the explanation.
The first part (the "if") corresponds to a
in ZZ
or R
, where
ZZ
is for the case of fractions of type 1/b
. The "elif" is for
a
already in the class fastfrac
; the "else" is for a
already
in the fraction field but not in the class fastfrac
.
Compared to this explanation, a
and b
are called top
and bot
in the code, so we have top.top
and top.bot
instead of a.top
and a.bot
, and we have bot
instead of b
.
The presence of the optional argument bot
with a default value of 1
takes care of the last item in the wishlist.
Let us illustrate using polynomials in one variable, for simplicity.
Define
sage: R.<x> = PolynomialRing(QQ)
and
sage: class fastfrac:
....:
....: def __init__(self, top, bot=1):
....:
....: if parent(top) == ZZ or parent(top) == R:
....: self.top = R(top)
....: self.bot = R(bot)
....: elif top.__class__ == fastfrac:
....: self.top = top.top
....: self.bot = top.bot * bot
....: else:
....: self.top = R(numerator(top))
....: self.bot = R(denominator(top)) * bot
....:
Then with
sage: a = x^2 + 1
sage: b = x^3 - 2
one can define their quotient as
sage: f = fastfrac(a, b)
and recover the "top" and "bottom" of the fraction:
sage: f.top
x^2 + 1
sage: f.bot
x^3 - 2
By contrast we might have tried to produce the quotient by dividing
a
by b
:
sage: g = a/b
and then we can't get the "top" and "bottom" in the same way:
sage: g.top
Traceback (most recent call last)
...
AttributeError: 'FractionFieldElement_1poly_field' object has no attribute 'top'
This is because the way it was defined, g
belongs
to Sage's implementation of the fraction field of R
,
and not to the class fastfrac
:
sage: g.parent()
Fraction Field of Univariate Polynomial Ring in x over Rational Field
But nothing to worry about, we can convert g
to the class fastfrac
easily:
sage: h = fastfrac(g)
and then we get, as expected:
sage: h.top
x^2 + 1
sage: h.bot
x^3 - 2
Welcome to Ask Sage! Thank you for your question!
Is this from the file group_prover.sage of the seqp256k1 repository of the bitcoin-core organisation? Or did you come across this code somewhere else?