A FractionField in the CoCoA Library is a fraction field of an effective
GCD domain. A FractionField offers all the operations of a ring
plus a
few new specific operations.
To create a new FractionField from a GCDDomain use NewFractionField:
FractionField QR = NewFractionField(R);
This creates a new object of type FractionField whose elements are
formal fractions of elements of R. CoCoA::FractionField is derived from
CoCoA::ring, so a FractionField is a ring
.
Given a FractionField the function BaseRing tells us which ring
it is
the FractionField of:
R == BaseRing(QR); // yields true
BaseRing(QR) produces an identical copy of R (not merely an isomorphic
ring
).
Given just a plain ring
S we can enquire whether S is in fact a
FractionField, and if so view it as such:
if (IsFractionField(S)) { FractionField FrF = AsFractionField(S); ... code using FrF ... }
Calling AsFractionField(S) when IsFractionField(S) is false will throw an exception of type CoCoAError with code ERR::NotFracField
Let r be a RingElem
belonging to a FractionField
QR. Then
num(r) -- gives a copy of the numerator of r as an element of BaseRing(QR) den(r) -- gives a copy of the denominator of r as an element of BaseRing(QR)
Note that the numerator and denominator are defined only upto multiples by a unit: so it is possible to have two elements of QR which are equal but which have different numerators and denominators, for instance, (x-1)/(x-2) = (1-x)/(2-x)
The class FractionField
is wholly analogous to the class ring
,
i.e. a reference counting smart pointer. The only difference is that
FractionField knows that the myRingPtr data member actually points to
an instance of a class derived from FractionFieldBase (and so can
safely apply a static_cast when the pointer value is accessed).
FractionFieldBase is an abstract class derived from RingBase
. It
adds a few pure virtual functions to those contained in RingBase
:
virtual const ring& BaseRing() const = 0; virtual void num(RawValue& n, ConstRawValue q) const = 0; // n is elem of BaseRing!! virtual void den(RawValue& d, ConstRawValue q) const = 0; // d is elem of BaseRing!! virtual const RingHom& EmbeddingHom() const = 0; virtual RingHom NewInducedHom(const RingHom&) const = 0;
BaseRing
returns a reference to the ring
(effective GCD
domain) supplied to the constructor.
num (resp. den) produces a copy of the numerator (resp. denominator) of q:
NOTE that the first arg belongs to BaseRing and NOT to the FractionField!
EmbeddingHom returns the embedding homomorphism from the BaseRing into
the FractionField; it actually returns a reference to a fixed
homomorpism held internallly.
InducedHom creates a new homomorpism from the FractionField to another
ring
S given a homomorpism from the BaseRing to S.
FractionFieldImpl implements a general fraction field. Its elements
are just pairs of RawValues belonging to the BaseRing (see the struct
FractionFieldElem). For this implementation the emphasis was clearly
on simplicity over speed (at least partly because we do not expect
FractionFieldImpl to be used much). For polynomials whose
coefficients lie in a FractionField we plan to implement a specific
ring
which uses a common denominator representation for the whole
polynomial. If you want to make this code faster, see some of the
comments in the bugs section.
Important: while fractions are guaranteed to be reduced (i.e. no common factor exists between numerator and denominator), it is rather hard to ensure that they are canonical since in general we can multiply numerator and denominator by any unit. See a bug comment about normalizing units.
The functions myNew are not exception safe: memory would be leaked if space for the numerator were successfully allocated while allocation for the denominator failed -- nobody would clean up the resources consumed by the numerator. Probably need a sort of auto_ptr for holding temporary bits of a value.
I don't like the names myGetNum and myGetDen. Hope to change them soon.
Should partial homomorphisms be allowed: e.g. from Q to Z/(3)? Mathematically it is a bit dodgy, but in practice all works out fine provided you don't divide by zero. I think it would be too useful (e.g. for chinese remaindering methods) to be banned. Given phi:Z->Z[x] it might be risky to induce Q->Z[x]; note that ker(phi)=0, so this is not a sufficient criterion!
In fact one could create a FractionFieldImpl of any integral domain (it just wouldn't be possible to cancel factors without a GCD). I'll wait until someone really needs it before allowing it.
It is not clear how to make the denominator positive when the GCD domain is Z (so the fraction field is Q). In general we would need the GCD domain to supply a normalizing unit: such a function could return 1 unless we have some special desire to normalize the denominator in a particular way. HERE'S A CONUNDRUM: FractionField(Q[x]) -- all rationals are units, and so we could end up with horrible representations like (22/7)/(22/7) instead of just 1. MUST FIX THIS!!
The printing function is TERRIBLE!
myIsRational
is incomplete: it will fail to recognise rationals whose
numer and denom have been multiplied by non-trivial units. BAD BUG!
Ironically myIsInteger
does work correctly.