/**************************************************************************** ** ** This file is part of GAP, a system for computational discrete algebra. ** ** Copyright of GAP belongs to its developers, whose names are too numerous ** to list here. Please refer to the COPYRIGHT file for details. ** ** SPDX-License-Identifier: GPL-2.0-or-later ** ** This file contains the functions for the artithmetic of rationals. ** ** Rationals are the union of integers and fractions. A fraction is a ** quotient of two integers where the denominator is relatively prime to the ** numerator. If in the description of a function we use the term rational ** this implies that the function is also capable of handling integers, ** though its function would usually be performed by a routine in the ** integer package. We will use the term fraction to stress the fact that ** something must not be an integer. ** ** A fraction is represented as a pair of two integers. The first is the ** numerator and the second is the denominator. This representation is ** always reduced, i.e., numerator and denominator are relative prime. The ** denominator is always positive and greater than 1. If it were 1 the ** fraction would be an integer and would be represented as integer. Since ** the denominator is always positive the numerator carries the sign of the ** fraction. ** ** It is very easy to see that for every fraction there is one unique ** reduced representation. Because of this comparisons of fractions are ** quite easy, we just compare numerator and denominator. Also numerator ** and denominator are as small as possible, reducing the effort to compute ** with them. Of course computing the reduced representation comes at a ** cost. After every arithmetic operation we have to compute the greatest ** common divisor of numerator and denominator, and divide them by the gcd. ** ** Effort has been made to improve efficiency by avoiding unnecessary gcd ** computations. Also if possible this package will compute two gcds of ** smaller integers instead of one gcd of larger integers. ** ** However no effort has been made to write special code for the case that ** some of the integers are small integers (i.e., less than 2^28). This ** would reduce the overhead introduced by the calls to the functions like ** 'SumInt', 'ProdInt' or 'GcdInt'.
*/
/**************************************************************************** ** *F TypeRat( <rat> ) . . . . . . . . . . . . . . . . . . type of a rational ** ** 'TypeRat' returns the type of the rational <rat>. ** ** 'TypeRat' is the function in 'TypeObjFuncs' for rationals.
*/ static Obj TYPE_RAT_POS; static Obj TYPE_RAT_NEG;
/**************************************************************************** ** *F EqRat( <opL>, <opR> ) . . . . . . . . . . . . . . test if <ratL> = <ratR> ** ** 'EqRat' returns 'true' if the two rationals <ratL> and <ratR> are equal ** and 'false' otherwise.
*/ staticInt EqRat(Obj opL, Obj opR)
{
Obj numL, denL; // numerator and denominator left
Obj numR, denR; // numerator and denominator right
CHECK_RAT(opL);
CHECK_RAT(opR);
// get numerator and denominator of the operands
numL = NUM_RAT(opL);
denL = DEN_RAT(opL);
numR = NUM_RAT(opR);
denR = DEN_RAT(opR);
// compare the numerators if ( ! EQ( numL, numR ) ) { return 0;
}
// compare the denominators if ( ! EQ( denL, denR ) ) { return 0;
}
// no differences found, they must be equal return 1;
}
/**************************************************************************** ** *F LtRat( <opL>, <opR> ) . . . . . . . . . . . . . . test if <ratL> < <ratR> ** ** 'LtRat' returns 'true' if the rational <ratL> is smaller than the ** rational <ratR> and 'false' otherwise. Either operand may be an integer.
*/ staticInt LtRat(Obj opL, Obj opR)
{
Obj numL, denL; // numerator and denominator left
Obj numR, denR; // numerator and denominator right
CHECK_RAT(opL);
CHECK_RAT(opR); // get numerator and denominator of the operands if ( TNUM_OBJ(opL) == T_RAT ) {
numL = NUM_RAT(opL);
denL = DEN_RAT(opL);
} else {
numL = opL;
denL = INTOBJ_INT(1);
} if ( TNUM_OBJ(opR) == T_RAT ) {
numR = NUM_RAT(opR);
denR = DEN_RAT(opR);
} else {
numR = opR;
denR = INTOBJ_INT(1);
}
// a / b < c / d <=> a d < c b return LtInt( ProdInt( numL, denR ), ProdInt( numR, denL ) );
}
/**************************************************************************** ** *F SumRat( <opL>, <opR> ) . . . . . . . . . . . . . . sum of two rationals ** ** 'SumRat' returns the sum of two rationals <opL> and <opR>. Either ** operand may also be an integer. The sum is reduced.
*/ static Obj SumRat(Obj opL, Obj opR)
{
Obj numL, denL; // numerator and denominator left
Obj numR, denR; // numerator and denominator right
Obj gcd1, gcd2; // gcd of denominators
Obj numS, denS; // numerator and denominator sum
Obj sum; // sum
CHECK_RAT(opL);
CHECK_RAT(opR); // get numerator and denominator of the operands if ( TNUM_OBJ(opL) == T_RAT ) {
numL = NUM_RAT(opL);
denL = DEN_RAT(opL);
} else {
numL = opL;
denL = INTOBJ_INT(1);
} if ( TNUM_OBJ(opR) == T_RAT ) {
numR = NUM_RAT(opR);
denR = DEN_RAT(opR);
} else {
numR = opR;
denR = INTOBJ_INT(1);
}
// find the gcd of the denominators
gcd1 = GcdInt( denL, denR );
// nothing can cancel if the gcd is 1 if (gcd1 == INTOBJ_INT(1)) {
numS = SumInt( ProdInt( numL, denR ), ProdInt( numR, denL ) );
denS = ProdInt( denL, denR );
}
/**************************************************************************** ** *F DiffRat( <opL>, <opR> ) . . . . . . . . . . . difference of two rationals ** ** 'DiffRat' returns the difference of two rationals <opL> and <opR>. ** Either operand may also be an integer. The difference is reduced.
*/ static Obj DiffRat(Obj opL, Obj opR)
{
Obj numL, denL; // numerator and denominator left
Obj numR, denR; // numerator and denominator right
Obj gcd1, gcd2; // gcd of denominators
Obj numD, denD; // numerator and denominator diff
Obj dif; // diff
CHECK_RAT(opL);
CHECK_RAT(opR); // get numerator and denominator of the operands if ( TNUM_OBJ(opL) == T_RAT ) {
numL = NUM_RAT(opL);
denL = DEN_RAT(opL);
} else {
numL = opL;
denL = INTOBJ_INT(1);
} if ( TNUM_OBJ(opR) == T_RAT ) {
numR = NUM_RAT(opR);
denR = DEN_RAT(opR);
} else {
numR = opR;
denR = INTOBJ_INT(1);
}
// find the gcd of the denominators
gcd1 = GcdInt( denL, denR );
// nothing can cancel if the gcd is 1 if (gcd1 == INTOBJ_INT(1)) {
numD = DiffInt( ProdInt( numL, denR ), ProdInt( numR, denL ) );
denD = ProdInt( denL, denR );
}
// make the fraction or, if possible, the integer if (denD != INTOBJ_INT(1)) {
dif = MakeRat(numD, denD);
} else {
dif = numD;
}
CHECK_RAT(dif); return dif;
}
/**************************************************************************** ** *F ProdRat( <opL>, <opR> ) . . . . . . . . . . . . product of two rationals ** ** 'ProdRat' returns the product of two rationals <opL> and <opR>. Either ** operand may also be an integer. The product is reduced.
*/ static Obj ProdRat(Obj opL, Obj opR)
{
Obj numL, denL; // numerator and denominator left
Obj numR, denR; // numerator and denominator right
Obj gcd1, gcd2; // gcd of denominators
Obj numP, denP; // numerator and denominator prod
Obj prd; // prod
CHECK_RAT(opL);
CHECK_RAT(opR); // get numerator and denominator of the operands if ( TNUM_OBJ(opL) == T_RAT ) {
numL = NUM_RAT(opL);
denL = DEN_RAT(opL);
} else {
numL = opL;
denL = INTOBJ_INT(1);
} if ( TNUM_OBJ(opR) == T_RAT ) {
numR = NUM_RAT(opR);
denR = DEN_RAT(opR);
} else {
numR = opR;
denR = INTOBJ_INT(1);
}
/**************************************************************************** ** *F QuoRat( <opL>, <opR> ) . . . . . . . . . . . . quotient of two rationals ** ** 'QuoRat' returns the quotient of two rationals <opL> and <opR>. Either ** operand may also be an integer. The quotient is reduced.
*/ static Obj QuoRat(Obj opL, Obj opR)
{
Obj numL, denL; // numerator and denominator left
Obj numR, denR; // numerator and denominator right
Obj gcd1, gcd2; // gcd of denominators
Obj numQ, denQ; // numerator and denominator Qrod
Obj quo; // Qrod
CHECK_RAT(opL);
CHECK_RAT(opR); // get numerator and denominator of the operands if ( TNUM_OBJ(opL) == T_RAT ) {
numL = NUM_RAT(opL);
denL = DEN_RAT(opL);
} else {
numL = opL;
denL = INTOBJ_INT(1);
} if ( TNUM_OBJ(opR) == T_RAT ) {
numR = NUM_RAT(opR);
denR = DEN_RAT(opR);
} else {
numR = opR;
denR = INTOBJ_INT(1);
}
// division by zero is an error if (numR == INTOBJ_INT(0)) {
ErrorMayQuit("Rational operations: must not be zero", 0, 0);
}
// we multiply the left numerator with the right denominator // so the right denominator should carry the sign of the right operand if ( IS_NEG_INT(numR) ) {
numR = AInvInt( numR );
denR = AInvInt( denR );
}
// nothing can cancel if the gcds are 1 if (gcd1 == INTOBJ_INT(1) && gcd2 == INTOBJ_INT(1)) {
numQ = ProdInt( numL, denR );
denQ = ProdInt( denL, numR );
}
// a little bit more difficult otherwise else {
numQ = ProdInt( QuoInt( numL, gcd1 ), QuoInt( denR, gcd2 ) );
denQ = ProdInt( QuoInt( denL, gcd2 ), QuoInt( numR, gcd1 ) );
}
// make the fraction or, if possible, the integer if (denQ != INTOBJ_INT(1)) {
quo = MakeRat(numQ, denQ);
} else {
quo = numQ;
}
CHECK_RAT(quo); return quo;
}
/**************************************************************************** ** *F ModRat( <opL>, <n> ) . . . . . . . . remainder of fraction mod integer ** ** 'ModRat' returns the remainder of the fraction <opL> modulo the integer ** <n>. The remainder is always an integer. ** ** '<r> / <s> mod <n>' yields the remainder of the fraction '<p> / <q>' ** modulo the integer '<n>', where '<p> / <q>' is the reduced form of ** '<r> / <s>'. ** ** The modular remainder of $r / s$ mod $n$ is defined to be the integer $k$ ** in $0 .. n-1$ such that $p = k q$ mod $n$, where $p = r / gcd(r, s)$ and ** $q = s / gcd(r, s)$. In particular, $1 / s$ mod $n$ is the modular ** inverse of $s$ modulo $n$, whenever $s$ and $n$ are relatively prime. ** ** Note that the remainder will not exist if $s / gcd(r, s)$ is not ** relatively prime to $n$. Note that $4 / 6$ mod $32$ does exist (and is ** $22$), even though $6$ is not invertible modulo $32$, because the $2$ ** cancels. ** ** Another possible definition of $r/s$ mod $n$ would be a rational $t/s$ ** such that $0 \<= t/s \< n$ and $r/s - t/s$ is a multiple of $n$. This is ** rarely needed while computing modular inverses is very useful.
*/ static Obj ModRat(Obj opL, Obj n)
{ // invert the denominator
Obj d = InverseModInt( DEN_RAT(opL), n );
// check whether the denominator of <opL> really was invertible mod <n> */ if ( d == Fail ) {
ErrorMayQuit( "ModRat: for / mod , /gcd(,) and must be coprime",
0, 0 );
}
// return the remainder return ModInt( ProdInt( NUM_RAT(opL), d ), n );
}
/**************************************************************************** ** *F PowRat( <opL>, <opR> ) . . . . . . raise a rational to an integer power ** ** 'PowRat' raises the rational <opL> to the power given by the integer ** <opR>. The power is reduced.
*/ static Obj PowRat(Obj opL, Obj opR)
{
Obj numP, denP; // numerator and denominator power
Obj pow; // power
CHECK_RAT(opL);
// if <opR> == 0 return 1 if (opR == INTOBJ_INT(0)) {
pow = INTOBJ_INT(1);
}
// if <opR> is negative and numerator is 1 just power the denominator elseif (NUM_RAT(opL) == INTOBJ_INT(1)) {
pow = PowInt( DEN_RAT(opL), AInvInt( opR ) );
}
// if <opR> is negative and numerator is -1 return (-1)^r * num(l) elseif (NUM_RAT(opL) == INTOBJ_INT(-1)) {
numP = PowInt( NUM_RAT(opL), AInvInt( opR ) );
denP = PowInt( DEN_RAT(opL), AInvInt( opR ) );
pow = ProdInt(numP, denP);
}
// if <opR> is negative do both powers, take care of the sign else {
numP = PowInt( DEN_RAT(opL), AInvInt( opR ) );
denP = PowInt( NUM_RAT(opL), AInvInt( opR ) ); if (IS_NEG_INT(denP)) {
numP = AInvInt(numP);
denP = AInvInt(denP);
}
pow = MakeRat(numP, denP);
}
CHECK_RAT(pow); return pow;
}
/**************************************************************************** ** *F FiltIS_RAT(<self>,<val>) . . . . . . . . . . . . . is a value a rational ** ** 'FiltIS_RAT' implements the internal function 'IsRat'. ** ** 'IsRat( <val> )' ** ** 'IsRat' returns 'true' if the value <val> is a rational and 'false' ** otherwise.
*/ static Obj IsRatFilt;
static Obj FiltIS_RAT(Obj self, Obj val)
{ // return 'true' if <val> is a rational and 'false' otherwise if ( TNUM_OBJ(val) == T_RAT || IS_INT(val) ) { returnTrue;
} elseif ( TNUM_OBJ(val) < FIRST_EXTERNAL_TNUM ) { returnFalse;
} else { return DoFilter( self, val );
}
}
/**************************************************************************** ** *F FuncNUMERATOR_RAT(<self>,<rat>) . . . . . . . . . numerator of a rational ** ** 'FuncNUMERATOR_RAT' implements the internal function 'NumeratorRat'. ** ** 'NumeratorRat( <rat> )' ** ** 'NumeratorRat' returns the numerator of the rational <rat>.
*/ static Obj FuncNUMERATOR_RAT(Obj self, Obj rat)
{
RequireRational(SELF_NAME, rat);
/**************************************************************************** ** *F InitKernel( <module> ) . . . . . . . . initialise kernel data structures
*/ staticInt InitKernel (
StructInitInfo * module )
{ // set the bag type names (for error messages and debugging)
InitBagNamesFromTable( BagNames );
// install the marking function // // MarkTwoSubBags() is faster for Gasman, but MarkAllSubBags() is // more space-efficient for the Boehm GC and does not incur a // speed penalty. #ifdef USE_GASMAN
InitMarkFuncBags( T_RAT, MarkTwoSubBags ); #else
InitMarkFuncBags( T_RAT, MarkAllSubBags ); #endif
// install the type functions
ImportGVarFromLibrary( "TYPE_RAT_POS", &TYPE_RAT_POS );
ImportGVarFromLibrary( "TYPE_RAT_NEG", &TYPE_RAT_NEG );
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.