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?