source: git/libpolys/polys/ext_fields/transext.cc @ a55ef0

spielwiese
Last change on this file since a55ef0 was a55ef0, checked in by Oleksandr Motsak <motsak@…>, 13 years ago
FIX: output the Frank's extension fields in the old Singular way (in order to avoid changing the testsuite result)
  • Property mode set to 100644
File size: 36.6 KB
Line 
1/****************************************
2*  Computer Algebra System SINGULAR     *
3****************************************/
4/* $Id$ */
5/*
6* ABSTRACT: numbers in a rational function field K(t_1, .., t_s) with
7*           transcendental variables t_1, ..., t_s, where s >= 1.
8*           Denoting the implemented coeffs object by cf, then these numbers
9*           are represented as quotients of polynomials living in the
10*           polynomial ring K[t_1, .., t_s] represented by cf->extring.
11*
12*           An element of K(t_1, .., t_s) may have numerous representations,
13*           due to the possibility of common polynomial factors in the
14*           numerator and denominator. This problem is handled by a
15*           cancellation heuristic: Each number "knows" its complexity
16*           which is 0 if and only if common factors have definitely been
17*           cancelled, and some positive integer otherwise.
18*           Each arithmetic operation of two numbers with complexities c1
19*           and c2 will result in a number of complexity c1 + c2 + some
20*           penalty (specific for each arithmetic operation; see constants
21*           in the *.h file). Whenever the resulting complexity exceeds a
22*           certain threshold (see constant in the *.h file), then the
23*           cancellation heuristic will call 'factory' to compute the gcd
24*           and cancel it out in the given number. (This definite cancel-
25*           lation will also be performed at the beginning of ntWrite,
26*           ensuring that any output is free of common factors.
27*           For the special case of K = Q (i.e., when computing over the
28*           rationals), this definite cancellation procedure will also take
29*           care of nested fractions: If there are fractional coefficients
30*           in the numerator or denominator of a number, then this number
31*           is being replaced by a quotient of two polynomials over Z, or
32*           - if the denominator is a constant - by a polynomial over Q.
33*/
34#define TRANSEXT_PRIVATES
35
36#include "config.h"
37#include <misc/auxiliary.h>
38
39#include <omalloc/omalloc.h>
40
41#include <reporter/reporter.h>
42
43#include <coeffs/coeffs.h>
44#include <coeffs/numbers.h>
45#include <coeffs/longrat.h>
46
47#include <polys/monomials/ring.h>
48#include <polys/monomials/p_polys.h>
49#include <polys/simpleideals.h>
50
51#ifdef HAVE_FACTORY
52#include <polys/clapsing.h>
53#endif
54
55#include "ext_fields/transext.h"
56
57
58/* constants for controlling the complexity of numbers */
59#define ADD_COMPLEXITY 1   /**< complexity increase due to + and - */
60#define MULT_COMPLEXITY 2   /**< complexity increase due to * and / */
61#define BOUND_COMPLEXITY 10   /**< maximum complexity of a number */
62
63/* some useful accessors for fractions: */
64#define IS0(f) (f == NULL) /**< TRUE iff n represents 0 in K(t_1, .., t_s) */
65
66#define DENIS1(f) (f->denominator == NULL) /**< TRUE iff den. represents 1 */
67#define NUMIS1(f) (p_IsConstant(f->numerator, cf->extRing) && \
68                   n_IsOne(p_GetCoeff(f->numerator, cf->extRing), \
69                           cf->extRing->cf))
70                   /**< TRUE iff num. represents 1 */
71#define COM(f) f->complexity
72
73
74#ifdef LDEBUG
75#define ntTest(a) ntDBTest(a,__FILE__,__LINE__,cf)
76BOOLEAN  ntDBTest(number a, const char *f, const int l, const coeffs r);
77#else
78#define ntTest(a) (TRUE)
79#endif
80
81/// Our own type!
82static const n_coeffType ID = n_transExt;
83
84/* polynomial ring in which the numerators and denominators of our
85   numbers live */
86#define ntRing cf->extRing
87
88/* coeffs object in which the coefficients of our numbers live;
89 * methods attached to ntCoeffs may be used to compute with the
90 * coefficients of our numbers, e.g., use ntCoeffs->nAdd to add
91 * coefficients of our numbers */
92#define ntCoeffs cf->extRing->cf
93
94
95
96extern omBin fractionObjectBin = omGetSpecBin(sizeof(fractionObject));
97
98/// forward declarations
99BOOLEAN  ntGreaterZero(number a, const coeffs cf); 
100BOOLEAN  ntGreater(number a, number b, const coeffs cf);
101BOOLEAN  ntEqual(number a, number b, const coeffs cf);
102BOOLEAN  ntIsOne(number a, const coeffs cf);
103BOOLEAN  ntIsMOne(number a, const coeffs cf);
104BOOLEAN  ntIsZero(number a, const coeffs cf);
105number   ntInit(int i, const coeffs cf);
106int      ntInt(number &a, const coeffs cf);
107number   ntNeg(number a, const coeffs cf);
108number   ntInvers(number a, const coeffs cf);
109number   ntAdd(number a, number b, const coeffs cf);
110number   ntSub(number a, number b, const coeffs cf);
111number   ntMult(number a, number b, const coeffs cf);
112number   ntDiv(number a, number b, const coeffs cf);
113void     ntPower(number a, int exp, number *b, const coeffs cf);
114number   ntCopy(number a, const coeffs cf);
115void     ntWrite(number &a, const coeffs cf);
116number   ntRePart(number a, const coeffs cf);
117number   ntImPart(number a, const coeffs cf);
118number   ntGetDenom(number &a, const coeffs cf);
119number   ntGetNumerator(number &a, const coeffs cf);
120number   ntGcd(number a, number b, const coeffs cf);
121number   ntLcm(number a, number b, const coeffs cf);
122int      ntSize(number a, const coeffs cf);
123void     ntDelete(number * a, const coeffs cf);
124void     ntCoeffWrite(const coeffs cf);
125number   ntIntDiv(number a, number b, const coeffs cf);
126const char * ntRead(const char *s, number *a, const coeffs cf);
127static BOOLEAN ntCoeffIsEqual(const coeffs cf, n_coeffType n, void * param);
128
129void heuristicGcdCancellation(number a, const coeffs cf);
130void definiteGcdCancellation(number a, const coeffs cf,
131                             BOOLEAN simpleTestsHaveAlreadyBeenPerformed);
132void handleNestedFractionsOverQ(fraction f, const coeffs cf);
133
134#ifdef LDEBUG
135BOOLEAN ntDBTest(number a, const char *f, const int l, const coeffs cf)
136{
137  assume(getCoeffType(cf) == ID);
138  fraction t = (fraction)a;
139  if (IS0(t)) return TRUE;
140  assume(NUM(t) != NULL);   /**< t != 0 ==> numerator(t) != 0 */
141  p_Test(NUM(t), ntRing);
142  if (!DENIS1(t)) p_Test(DEN(t), ntRing);
143  return TRUE;
144}
145#endif
146
147/* returns the bottom field in this field extension tower; if the tower
148   is flat, i.e., if there is no extension, then r itself is returned;
149   as a side-effect, the counter 'height' is filled with the height of
150   the extension tower (in case the tower is flat, 'height' is zero) */
151static coeffs nCoeff_bottom(const coeffs r, int &height)
152{
153  assume(r != NULL);
154  coeffs cf = r;
155  height = 0;
156  while (nCoeff_is_Extension(cf))
157  {
158    assume(cf->extRing != NULL); assume(cf->extRing->cf != NULL);
159    cf = cf->extRing->cf;
160    height++;
161  }
162  return cf;
163}
164
165BOOLEAN ntIsZero(number a, const coeffs cf)
166{
167  ntTest(a);
168  return (IS0(a));
169}
170
171void ntDelete(number * a, const coeffs cf)
172{
173  fraction f = (fraction)(*a);
174  if (IS0(f)) return;
175  p_Delete(&NUM(f), ntRing);
176  if (!DENIS1(f)) p_Delete(&DEN(f), ntRing);
177  omFreeBin((ADDRESS)f, fractionObjectBin);
178  *a = NULL;
179}
180
181BOOLEAN ntEqual(number a, number b, const coeffs cf)
182{
183  ntTest(a); ntTest(b);
184 
185  /// simple tests
186  if (a == b) return TRUE;
187  if ((IS0(a)) && (!IS0(b))) return FALSE;
188  if ((IS0(b)) && (!IS0(a))) return FALSE;
189 
190  /// cheap test if gcd's have been cancelled in both numbers
191  fraction fa = (fraction)a;
192  fraction fb = (fraction)b;
193  if ((COM(fa) == 1) && (COM(fb) == 1))
194  {
195    poly f = p_Add_q(p_Copy(NUM(fa), ntRing),
196                     p_Neg(p_Copy(NUM(fb), ntRing), ntRing),
197                     ntRing);
198    if (f != NULL) { p_Delete(&f, ntRing); return FALSE; }
199    if (DENIS1(fa) && DENIS1(fb))  return TRUE;
200    if (DENIS1(fa) && !DENIS1(fb)) return FALSE;
201    if (!DENIS1(fa) && DENIS1(fb)) return FALSE;
202    f = p_Add_q(p_Copy(DEN(fa), ntRing),
203                p_Neg(p_Copy(DEN(fb), ntRing), ntRing),
204                ntRing);
205    if (f != NULL) { p_Delete(&f, ntRing); return FALSE; }
206    return TRUE;
207  }
208 
209  /* default: the more expensive multiplication test
210              a/b = c/d  <==>  a*d = b*c */
211  poly f = p_Copy(NUM(fa), ntRing);
212  if (!DENIS1(fb)) f = p_Mult_q(f, p_Copy(DEN(fb), ntRing), ntRing);
213  poly g = p_Copy(NUM(fb), ntRing);
214  if (!DENIS1(fa)) g = p_Mult_q(g, p_Copy(DEN(fa), ntRing), ntRing);
215  poly h = p_Add_q(f, p_Neg(g, ntRing), ntRing);
216  if (h == NULL) return TRUE;
217  else
218  {
219    p_Delete(&h, ntRing);
220    return FALSE;
221  }
222}
223
224number ntCopy(number a, const coeffs cf)
225{
226  ntTest(a);
227  if (IS0(a)) return NULL;
228  fraction f = (fraction)a;
229  poly g = p_Copy(NUM(f), ntRing);
230  poly h = NULL; if (!DENIS1(f)) h = p_Copy(DEN(f), ntRing);
231  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
232  NUM(result) = g;
233  DEN(result) = h;
234  COM(result) = COM(f);
235  return (number)result;
236}
237
238number ntGetNumerator(number &a, const coeffs cf)
239{
240  ntTest(a);
241  definiteGcdCancellation(a, cf, FALSE);
242  if (IS0(a)) return NULL;
243  fraction f = (fraction)a;
244  poly g = p_Copy(NUM(f), ntRing);
245  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
246  NUM(result) = g;
247  DEN(result) = NULL;
248  COM(result) = 0;
249  return (number)result;
250}
251
252number ntGetDenom(number &a, const coeffs cf)
253{
254  ntTest(a);
255  definiteGcdCancellation(a, cf, FALSE);
256  fraction f = (fraction)a;
257  poly g;
258  if (IS0(f) || DENIS1(f)) g = p_One(ntRing);
259  else g = p_Copy(DEN(f), ntRing);
260  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
261  NUM(result) = g;
262  DEN(result) = NULL;
263  COM(result) = 0;
264  return (number)result;
265}
266
267BOOLEAN ntIsOne(number a, const coeffs cf)
268{
269  ntTest(a);
270  definiteGcdCancellation(a, cf, FALSE);
271  fraction f = (fraction)a;
272  return DENIS1(f) && NUMIS1(f);
273}
274
275BOOLEAN ntIsMOne(number a, const coeffs cf)
276{
277  ntTest(a);
278  definiteGcdCancellation(a, cf, FALSE);
279  fraction f = (fraction)a;
280  if (!DENIS1(f)) return FALSE;
281  poly g = NUM(f);
282  if (!p_IsConstant(g, ntRing)) return FALSE;
283  return n_IsMOne(p_GetCoeff(g, ntRing), ntCoeffs);
284}
285
286/// this is in-place, modifies a
287number ntNeg(number a, const coeffs cf)
288{
289  ntTest(a);
290  if (!IS0(a))
291  {
292    fraction f = (fraction)a;
293    NUM(f) = p_Neg(NUM(f), ntRing);
294  }
295  return a;
296}
297
298number ntImPart(number a, const coeffs cf)
299{
300  ntTest(a);
301  return NULL;
302}
303
304number ntInit(int i, const coeffs cf)
305{
306  if (i == 0) return NULL;
307  else
308  {
309    fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
310    NUM(result) = p_ISet(i, ntRing);
311    DEN(result) = NULL;
312    COM(result) = 0;
313    return (number)result;
314  }
315}
316
317int ntInt(number &a, const coeffs cf)
318{
319  ntTest(a);
320  if (IS0(a)) return 0;
321  definiteGcdCancellation(a, cf, FALSE);
322  fraction f = (fraction)a;
323  if (!DENIS1(f)) return 0;
324  if (!p_IsConstant(NUM(f), ntRing)) return 0;
325  return n_Int(p_GetCoeff(NUM(f), ntRing), ntCoeffs);
326}
327
328/* This method will only consider the numerators of a and b, without
329   cancelling gcd's before.
330   Moreover it may return TRUE only if one or both numerators
331   are zero or if their degrees are equal. Then TRUE is returned iff
332   coeff(numerator(a)) > coeff(numerator(b));
333   In all other cases, FALSE will be returned. */
334BOOLEAN ntGreater(number a, number b, const coeffs cf)
335{
336  ntTest(a); ntTest(b);
337  number aNumCoeff = NULL; int aNumDeg = 0;
338  number bNumCoeff = NULL; int bNumDeg = 0;
339  if (!IS0(a))
340  {
341    fraction fa = (fraction)a;
342    aNumDeg = p_Totaldegree(NUM(fa), ntRing);
343    aNumCoeff = p_GetCoeff(NUM(fa), ntRing);
344  }
345  if (!IS0(b))
346  {
347    fraction fb = (fraction)b;
348    bNumDeg = p_Totaldegree(NUM(fb), ntRing);
349    bNumCoeff = p_GetCoeff(NUM(fb), ntRing);
350  }
351  if (aNumDeg != bNumDeg) return FALSE;
352  else return n_Greater(aNumCoeff, bNumCoeff, ntCoeffs);
353}
354
355/* this method will only consider the numerator of a, without cancelling
356   the gcd before;
357   returns TRUE iff the leading coefficient of the numerator of a is > 0
358                    or the leading term of the numerator of a is not a
359                    constant */
360BOOLEAN ntGreaterZero(number a, const coeffs cf)
361{
362  ntTest(a);
363  if (IS0(a)) return FALSE;
364  fraction f = (fraction)a;
365  poly g = NUM(f);
366  return (n_GreaterZero(p_GetCoeff(g, ntRing), ntCoeffs) ||
367          (!p_LmIsConstant(g, ntRing)));
368}
369
370void ntCoeffWrite(const coeffs cf)
371{
372  assume( cf != NULL );
373
374  const ring A = cf->extRing;
375
376  assume( A != NULL );
377  assume( A->cf != NULL );
378
379  n_CoeffWrite(A->cf);
380
381//  rWrite(A);
382
383  const int P = rVar(A);
384  assume( P > 0 );
385 
386  Print("//   %d parameter    : ", P);
387 
388  for (int nop=0; nop < P; nop ++)
389    Print("%s ", rRingVar(nop, A));
390
391  assume( A->minideal == NULL );
392 
393  PrintS("\n//   minpoly        : 0\n");
394
395
396/*
397  PrintS("//   Coefficients live in the rational function field\n");
398  Print("//   K(");
399  for (int i = 0; i < rVar(ntRing); i++)
400  {
401    if (i > 0) PrintS(" ");
402    Print("%s", rRingVar(i, ntRing));
403  }
404  PrintS(") with\n");
405  PrintS("//   K: "); n_CoeffWrite(cf->extRing->cf);
406*/
407}
408
409number ntAdd(number a, number b, const coeffs cf)
410{
411  ntTest(a); ntTest(b);
412  if (IS0(a)) return ntCopy(b, cf);
413  if (IS0(b)) return ntCopy(a, cf);
414 
415  fraction fa = (fraction)a;
416  fraction fb = (fraction)b;
417 
418  poly g = p_Copy(NUM(fa), ntRing);
419  if (!DENIS1(fb)) g = p_Mult_q(g, p_Copy(DEN(fb), ntRing), ntRing);
420  poly h = p_Copy(NUM(fb), ntRing);
421  if (!DENIS1(fa)) h = p_Mult_q(h, p_Copy(DEN(fa), ntRing), ntRing);
422  g = p_Add_q(g, h, ntRing);
423 
424  if (g == NULL) return NULL;
425 
426  poly f;
427  if      (DENIS1(fa) && DENIS1(fb))  f = NULL;
428  else if (!DENIS1(fa) && DENIS1(fb)) f = p_Copy(DEN(fa), ntRing);
429  else if (DENIS1(fa) && !DENIS1(fb)) f = p_Copy(DEN(fb), ntRing);
430  else /* both denom's are != 1 */    f = p_Mult_q(p_Copy(DEN(fa), ntRing),
431                                                   p_Copy(DEN(fb), ntRing),
432                                                   ntRing);
433 
434  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
435  NUM(result) = g;
436  DEN(result) = f;
437  COM(result) = COM(fa) + COM(fb) + ADD_COMPLEXITY;
438  heuristicGcdCancellation((number)result, cf);
439  return (number)result;
440}
441
442number ntSub(number a, number b, const coeffs cf)
443{
444  ntTest(a); ntTest(b);
445  if (IS0(a)) return ntNeg(ntCopy(b, cf), cf);
446  if (IS0(b)) return ntCopy(a, cf);
447 
448  fraction fa = (fraction)a;
449  fraction fb = (fraction)b;
450 
451  poly g = p_Copy(NUM(fa), ntRing);
452  if (!DENIS1(fb)) g = p_Mult_q(g, p_Copy(DEN(fb), ntRing), ntRing);
453  poly h = p_Copy(NUM(fb), ntRing);
454  if (!DENIS1(fa)) h = p_Mult_q(h, p_Copy(DEN(fa), ntRing), ntRing);
455  g = p_Add_q(g, p_Neg(h, ntRing), ntRing);
456 
457  if (g == NULL) return NULL;
458 
459  poly f;
460  if      (DENIS1(fa) && DENIS1(fb))  f = NULL;
461  else if (!DENIS1(fa) && DENIS1(fb)) f = p_Copy(DEN(fa), ntRing);
462  else if (DENIS1(fa) && !DENIS1(fb)) f = p_Copy(DEN(fb), ntRing);
463  else /* both den's are != 1 */      f = p_Mult_q(p_Copy(DEN(fa), ntRing),
464                                                   p_Copy(DEN(fb), ntRing),
465                                                   ntRing);
466 
467  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
468  NUM(result) = g;
469  DEN(result) = f;
470  COM(result) = COM(fa) + COM(fb) + ADD_COMPLEXITY;
471  heuristicGcdCancellation((number)result, cf);
472  return (number)result;
473}
474
475number ntMult(number a, number b, const coeffs cf)
476{
477  ntTest(a); ntTest(b);
478  if (IS0(a) || IS0(b)) return NULL;
479 
480  fraction fa = (fraction)a;
481  fraction fb = (fraction)b;
482 
483  poly g = p_Copy(NUM(fa), ntRing);
484  poly h = p_Copy(NUM(fb), ntRing);
485  g = p_Mult_q(g, h, ntRing);
486 
487  if (g == NULL) return NULL;   /* may happen due to zero divisors */
488 
489  poly f;
490  if      (DENIS1(fa) && DENIS1(fb))  f = NULL;
491  else if (!DENIS1(fa) && DENIS1(fb)) f = p_Copy(DEN(fa), ntRing);
492  else if (DENIS1(fa) && !DENIS1(fb)) f = p_Copy(DEN(fb), ntRing);
493  else /* both den's are != 1 */      f = p_Mult_q(p_Copy(DEN(fa), ntRing),
494                                                   p_Copy(DEN(fb), ntRing),
495                                                   ntRing);
496 
497  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
498  NUM(result) = g;
499  DEN(result) = f;
500  COM(result) = COM(fa) + COM(fb) + MULT_COMPLEXITY;
501  heuristicGcdCancellation((number)result, cf);
502  return (number)result;
503}
504
505number ntDiv(number a, number b, const coeffs cf)
506{
507  ntTest(a); ntTest(b);
508  if (IS0(a)) return NULL;
509  if (IS0(b)) WerrorS(nDivBy0);
510 
511  fraction fa = (fraction)a;
512  fraction fb = (fraction)b;
513 
514  poly g = p_Copy(NUM(fa), ntRing);
515  if (!DENIS1(fb)) g = p_Mult_q(g, p_Copy(DEN(fb), ntRing), ntRing);
516 
517  if (g == NULL) return NULL;   /* may happen due to zero divisors */
518 
519  poly f = p_Copy(NUM(fb), ntRing);
520  if (!DENIS1(fa)) f = p_Mult_q(f, p_Copy(DEN(fa), ntRing), ntRing);
521 
522  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
523  NUM(result) = g;
524  DEN(result) = f;
525  COM(result) = COM(fa) + COM(fb) + MULT_COMPLEXITY;
526  heuristicGcdCancellation((number)result, cf);
527  return (number)result;
528}
529
530/* 0^0 = 0;
531   for |exp| <= 7 compute power by a simple multiplication loop;
532   for |exp| >= 8 compute power along binary presentation of |exp|, e.g.
533      p^13 = p^1 * p^4 * p^8, where we utilise that
534      p^(2^(k+1)) = p^(2^k) * p^(2^k);
535   intermediate cancellation is controlled by the in-place method
536   heuristicGcdCancellation; see there.
537*/
538void ntPower(number a, int exp, number *b, const coeffs cf)
539{
540  ntTest(a);
541 
542  /* special cases first */
543  if (IS0(a))
544  {
545    if (exp >= 0) *b = NULL;
546    else          WerrorS(nDivBy0);
547  }
548  else if (exp ==  0) *b = ntInit(1, cf);
549  else if (exp ==  1) *b = ntCopy(a, cf);
550  else if (exp == -1) *b = ntInvers(a, cf);
551 
552  int expAbs = exp; if (expAbs < 0) expAbs = -expAbs;
553 
554  /* now compute a^expAbs */
555  number pow; number t;
556  if (expAbs <= 7)
557  {
558    pow = ntCopy(a, cf);
559    for (int i = 2; i <= expAbs; i++)
560    {
561      t = ntMult(pow, a, cf);
562      ntDelete(&pow, cf);
563      pow = t;
564      heuristicGcdCancellation(pow, cf);
565    }
566  }
567  else
568  {
569    pow = ntInit(1, cf);
570    number factor = ntCopy(a, cf);
571    while (expAbs != 0)
572    {
573      if (expAbs & 1)
574      {
575        t = ntMult(pow, factor, cf);
576        ntDelete(&pow, cf);
577        pow = t;
578        heuristicGcdCancellation(pow, cf);
579      }
580      expAbs = expAbs / 2;
581      if (expAbs != 0)
582      {
583        t = ntMult(factor, factor, cf);
584        ntDelete(&factor, cf);
585        factor = t;
586        heuristicGcdCancellation(factor, cf);
587      }
588    }
589    ntDelete(&factor, cf);
590  }
591 
592  /* invert if original exponent was negative */
593  if (exp < 0)
594  {
595    t = ntInvers(pow, cf);
596    ntDelete(&pow, cf);
597    pow = t;
598  }
599  *b = pow;
600}
601
602/* assumes that cf represents the rationals, i.e. Q, and will only
603   be called in that case;
604   assumes furthermore that f != NULL and that the denominator of f != 1;
605   generally speaking, this method removes denominators in the rational
606   coefficients of the numerator and denominator of 'a';
607   more concretely, the following normalizations will be performed,
608   where t^alpha denotes a monomial in the transcendental variables t_k
609   (1) if 'a' is of the form
610          (sum_alpha a_alpha/b_alpha * t^alpha)
611          -------------------------------------
612            (sum_beta c_beta/d_beta * t^beta)
613       with integers a_alpha, b_alpha, c_beta, d_beta, then both the
614       numerator and the denominator will be multiplied by the LCM of
615       the b_alpha's and the d_beta's (if this LCM is != 1),
616   (2) if 'a' is - e.g. after having performed step (1) - of the form
617          (sum_alpha a_alpha * t^alpha)
618          -----------------------------
619            (sum_beta c_beta * t^beta)
620       with integers a_alpha, c_beta, and with a non-constant denominator,
621       then both the numerator and the denominator will be divided by the
622       GCD of the a_alpha's and the c_beta's (if this GCD is != 1),
623   (3) if 'a' is - e.g. after having performed steps (1) and (2) - of the
624       form
625          (sum_alpha a_alpha * t^alpha)
626          -----------------------------
627                        c
628       with integers a_alpha, and c != 1, then 'a' will be replaced by
629       (sum_alpha a_alpha/c * t^alpha);
630   this procedure does not alter COM(f) (this has to be done by the
631   calling procedure);
632   modifies f */
633void handleNestedFractionsOverQ(fraction f, const coeffs cf)
634{
635  assume(nCoeff_is_Q(ntCoeffs));
636  assume(!IS0(f));
637  assume(!DENIS1(f));
638 
639  if (!p_IsConstant(DEN(f), ntRing))
640  { /* step (1); see documentation of this procedure above */
641    p_Normalize(NUM(f), ntRing);
642    p_Normalize(DEN(f), ntRing);
643    number lcmOfDenominators = n_Init(1, ntCoeffs);
644    number c; number tmp;
645    poly p = NUM(f);
646    /* careful when using n_Lcm!!! It computes the lcm of the numerator
647       of the 1st argument and the denominator of the 2nd!!! */
648    while (p != NULL)
649    {
650      c = p_GetCoeff(p, ntRing);
651      tmp = n_Lcm(lcmOfDenominators, c, ntCoeffs);
652      n_Delete(&lcmOfDenominators, ntCoeffs);
653      lcmOfDenominators = tmp;
654      pIter(p);
655    }
656    p = DEN(f);
657    while (p != NULL)
658    {
659      c = p_GetCoeff(p, ntRing);
660      tmp = n_Lcm(lcmOfDenominators, c, ntCoeffs);
661      n_Delete(&lcmOfDenominators, ntCoeffs);
662      lcmOfDenominators = tmp;
663      pIter(p);
664    }
665    if (!n_IsOne(lcmOfDenominators, ntCoeffs))
666    { /* multiply NUM(f) and DEN(f) with lcmOfDenominators */
667      NUM(f) = p_Mult_nn(NUM(f), lcmOfDenominators, ntRing);
668      p_Normalize(NUM(f), ntRing);
669      DEN(f) = p_Mult_nn(DEN(f), lcmOfDenominators, ntRing);
670      p_Normalize(DEN(f), ntRing);
671    }
672    n_Delete(&lcmOfDenominators, ntCoeffs);
673    if (!p_IsConstant(DEN(f), ntRing))
674    { /* step (2); see documentation of this procedure above */
675      p = NUM(f);
676      number gcdOfCoefficients = n_Copy(p_GetCoeff(p, ntRing), ntCoeffs);
677      pIter(p);
678      while ((p != NULL) && (!n_IsOne(gcdOfCoefficients, ntCoeffs)))
679      {
680        c = p_GetCoeff(p, ntRing);
681        tmp = n_Gcd(c, gcdOfCoefficients, ntCoeffs);
682        n_Delete(&gcdOfCoefficients, ntCoeffs);
683        gcdOfCoefficients = tmp;
684        pIter(p);
685      }
686      p = DEN(f);
687      while ((p != NULL) && (!n_IsOne(gcdOfCoefficients, ntCoeffs)))
688      {
689        c = p_GetCoeff(p, ntRing);
690        tmp = n_Gcd(c, gcdOfCoefficients, ntCoeffs);
691        n_Delete(&gcdOfCoefficients, ntCoeffs);
692        gcdOfCoefficients = tmp;
693        pIter(p);
694      }
695      if (!n_IsOne(gcdOfCoefficients, ntCoeffs))
696      { /* divide NUM(f) and DEN(f) by gcdOfCoefficients */
697        number inverseOfGcdOfCoefficients = n_Invers(gcdOfCoefficients,
698                                                     ntCoeffs);
699        NUM(f) = p_Mult_nn(NUM(f), inverseOfGcdOfCoefficients, ntRing);
700        p_Normalize(NUM(f), ntRing);
701        DEN(f) = p_Mult_nn(DEN(f), inverseOfGcdOfCoefficients, ntRing);
702        p_Normalize(DEN(f), ntRing);
703        n_Delete(&inverseOfGcdOfCoefficients, ntCoeffs);
704      }
705      n_Delete(&gcdOfCoefficients, ntCoeffs);
706    }
707  }
708  if (p_IsConstant(DEN(f), ntRing) &&
709      (!n_IsOne(p_GetCoeff(DEN(f), ntRing), ntCoeffs)))
710  { /* step (3); see documentation of this procedure above */
711    number inverseOfDen = n_Invers(p_GetCoeff(DEN(f), ntRing), ntCoeffs);
712    NUM(f) = p_Mult_nn(NUM(f), inverseOfDen, ntRing);
713    n_Delete(&inverseOfDen, ntCoeffs);
714    p_Delete(&DEN(f), ntRing);
715    DEN(f) = NULL;
716  }
717 
718  /* Now, due to the above computations, DEN(f) may have become the
719     1-polynomial which needs to be represented by NULL: */
720  if ((DEN(f) != NULL) &&
721      p_IsConstant(DEN(f), ntRing) &&
722      n_IsOne(p_GetCoeff(DEN(f), ntRing), ntCoeffs))
723  {
724    p_Delete(&DEN(f), ntRing); DEN(f) = NULL;
725  }
726}
727
728/* modifies a */
729void heuristicGcdCancellation(number a, const coeffs cf)
730{
731  ntTest(a);
732  if (IS0(a)) return;
733 
734  fraction f = (fraction)a;
735  if (DENIS1(f) || NUMIS1(f)) { COM(f) = 0; return; }
736 
737  /* check whether NUM(f) = DEN(f), and - if so - replace 'a' by 1 */
738  if (p_EqualPolys(NUM(f), DEN(f), ntRing))
739  { /* numerator and denominator are both != 1 */
740    p_Delete(&NUM(f), ntRing); NUM(f) = p_ISet(1, ntRing);
741    p_Delete(&DEN(f), ntRing); DEN(f) = NULL;
742    COM(f) = 0;
743    return;
744  }
745 
746  if (COM(f) <= BOUND_COMPLEXITY) return;
747  else definiteGcdCancellation(a, cf, TRUE);
748}
749
750/* modifies a */
751void definiteGcdCancellation(number a, const coeffs cf,
752                             BOOLEAN simpleTestsHaveAlreadyBeenPerformed)
753{
754  ntTest(a);
755 
756  fraction f = (fraction)a;
757 
758  if (!simpleTestsHaveAlreadyBeenPerformed)
759  {
760    if (IS0(a)) return;
761    if (DENIS1(f) || NUMIS1(f)) { COM(f) = 0; return; }
762 
763    /* check whether NUM(f) = DEN(f), and - if so - replace 'a' by 1 */
764    if (p_EqualPolys(NUM(f), DEN(f), ntRing))
765    { /* numerator and denominator are both != 1 */
766      p_Delete(&NUM(f), ntRing); NUM(f) = p_ISet(1, ntRing);
767      p_Delete(&DEN(f), ntRing); DEN(f) = NULL;
768      COM(f) = 0;
769      return;
770    }
771  }
772 
773#ifdef HAVE_FACTORY 
774  /* singclap_gcd destroys its arguments; we hence need copies: */
775  poly pNum = p_Copy(NUM(f), ntRing);
776  poly pDen = p_Copy(DEN(f), ntRing);
777 
778  /* Note that, over Q, singclap_gcd will remove the denominators in all
779     rational coefficients of pNum and pDen, before starting to compute
780     the gcd. Thus, we do not need to ensure that the coefficients of
781     pNum and pDen live in Z; they may well be elements of Q\Z. */
782  poly pGcd = singclap_gcd(pNum, pDen, cf->extRing);
783  if (p_IsConstant(pGcd, ntRing) &&
784      n_IsOne(p_GetCoeff(pGcd, ntRing), ntCoeffs))
785  { /* gcd = 1; nothing to cancel;
786       Suppose the given rational function field is over Q. Although the
787       gcd is 1, we may have produced fractional coefficients in NUM(f),
788       DEN(f), or both, due to previous arithmetics. The next call will
789       remove those nested fractions, in case there are any. */
790    if (nCoeff_is_Q(ntCoeffs)) handleNestedFractionsOverQ(f, cf);
791  }
792  else
793  { /* We divide both NUM(f) and DEN(f) by the gcd which is known
794       to be != 1. */
795    poly newNum = singclap_pdivide(NUM(f), pGcd, ntRing);
796    p_Delete(&NUM(f), ntRing);
797    NUM(f) = newNum;
798    poly newDen = singclap_pdivide(DEN(f), pGcd, ntRing);
799    p_Delete(&DEN(f), ntRing);
800    DEN(f) = newDen;
801    if (p_IsConstant(DEN(f), ntRing) &&
802        n_IsOne(p_GetCoeff(DEN(f), ntRing), ntCoeffs))
803    {
804      /* DEN(f) = 1 needs to be represented by NULL! */
805      p_Delete(&DEN(f), ntRing);
806      newDen = NULL;
807    }
808    else
809    { /* Note that over Q, by cancelling the gcd, we may have produced
810         fractional coefficients in NUM(f), DEN(f), or both. The next
811         call will remove those nested fractions, in case there are
812         any. */
813      if (nCoeff_is_Q(ntCoeffs)) handleNestedFractionsOverQ(f, cf);
814    }
815  }
816  COM(f) = 0;
817  p_Delete(&pGcd, ntRing);
818#endif /* HAVE_FACTORY */
819}
820
821/* modifies a */
822void ntWrite(number &a, const coeffs cf)
823{
824  ntTest(a);
825  definiteGcdCancellation(a, cf, FALSE);
826  if (IS0(a))
827    StringAppendS("0");
828  else
829  {
830    fraction f = (fraction)a;
831    BOOLEAN useBrackets = (!p_IsConstant(NUM(f), ntRing)) ||
832                          (!n_GreaterZero(p_GetCoeff(NUM(f), ntRing),
833                                          ntCoeffs));
834    if (useBrackets) StringAppendS("(");
835    p_String0(NUM(f), ntRing, ntRing);
836    if (useBrackets) StringAppendS(")");
837    if (!DENIS1(f))
838    {
839      StringAppendS("/");
840      useBrackets = (!p_IsConstant(DEN(f), ntRing)) ||
841                    (!n_GreaterZero(p_GetCoeff(DEN(f), ntRing), ntCoeffs));
842      if (useBrackets) StringAppendS("(");
843      p_String0(DEN(f), ntRing, ntRing);
844      if (useBrackets) StringAppendS(")");
845    }
846  }
847}
848
849const char * ntRead(const char *s, number *a, const coeffs cf)
850{
851  poly p;
852  const char * result = p_Read(s, p, ntRing);
853  if (p == NULL) { *a = NULL; return result; }
854  else
855  {
856    fraction f = (fraction)omAlloc0Bin(fractionObjectBin);
857    NUM(f) = p;
858    DEN(f) = NULL;
859    COM(f) = 0;
860    *a = (number)f;
861    return result;
862  }
863}
864
865/* expects *param to be castable to TransExtInfo */
866static BOOLEAN ntCoeffIsEqual(const coeffs cf, n_coeffType n, void * param)
867{
868  if (ID != n) return FALSE;
869  TransExtInfo *e = (TransExtInfo *)param;
870  /* for rational function fields we expect the underlying
871     polynomial rings to be IDENTICAL, i.e. the SAME OBJECT;
872     this expectation is based on the assumption that we have properly
873     registered cf and perform reference counting rather than creating
874     multiple copies of the same coefficient field/domain/ring */
875  return (ntRing == e->r);
876}
877
878number ntLcm(number a, number b, const coeffs cf)
879{
880  ntTest(a); ntTest(b);
881  /* TO BE IMPLEMENTED!
882     for the time, we simply return NULL, representing the number zero */
883  Print("// TO BE IMPLEMENTED: transext.cc:ntLcm\n");
884  return NULL;
885}
886
887number ntGcd(number a, number b, const coeffs cf)
888{
889  ntTest(a); ntTest(b);
890  /* TO BE IMPLEMENTED!
891     for the time, we simply return NULL, representing the number zero */
892  Print("// TO BE IMPLEMENTED: transext.cc:ntGcd\n");
893  return NULL;
894}
895
896int ntSize(number a, const coeffs cf)
897{
898  ntTest(a);
899  if (IS0(a)) return -1;
900  /* this has been taken from the old implementation of field extensions,
901     where we computed the sum of the degrees and the numbers of terms in
902     the numerator and denominator of a; so we leave it at that, for the
903     time being */
904  fraction f = (fraction)a;
905  poly p = NUM(f);
906  int noOfTerms = 0;
907  int numDegree = 0;
908  while (p != NULL)
909  {
910    noOfTerms++;
911    int d = 0;
912    for (int i = 1; i <= rVar(ntRing); i++)
913      d += p_GetExp(p, i, ntRing);
914    if (d > numDegree) numDegree = d;
915    pIter(p);
916  }
917  int denDegree = 0;
918  if (!DENIS1(f))
919  {
920    p = DEN(f);
921    while (p != NULL)
922    {
923      noOfTerms++;
924      int d = 0;
925      for (int i = 1; i <= rVar(ntRing); i++)
926        d += p_GetExp(p, i, ntRing);
927      if (d > denDegree) denDegree = d;
928      pIter(p);
929    }
930  }
931  return numDegree + denDegree + noOfTerms;
932}
933
934number ntInvers(number a, const coeffs cf)
935{
936  ntTest(a);
937  if (IS0(a)) WerrorS(nDivBy0);
938  fraction f = (fraction)a;
939  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
940  poly g;
941  if (DENIS1(f)) g = p_One(ntRing);
942  else           g = p_Copy(DEN(f), ntRing);
943  NUM(result) = g;
944  DEN(result) = p_Copy(NUM(f), ntRing);
945  COM(result) = COM(f);
946  return (number)result;
947}
948
949/* assumes that src = Q, dst = Q(t_1, ..., t_s) */
950number ntMap00(number a, const coeffs src, const coeffs dst)
951{
952  if (n_IsZero(a, src)) return NULL;
953  assume(src == dst->extRing->cf);
954  poly p = p_One(dst->extRing);
955  p_SetCoeff(p, ntCopy(a, src), dst->extRing);
956  fraction f = (fraction)omAlloc0Bin(fractionObjectBin);
957  NUM(f) = p; DEN(f) = NULL; COM(f) = 0;
958  return (number)f;
959}
960
961/* assumes that src = Z/p, dst = Q(t_1, ..., t_s) */
962number ntMapP0(number a, const coeffs src, const coeffs dst)
963{
964  if (n_IsZero(a, src)) return NULL;
965  /* mapping via intermediate int: */
966  int n = n_Int(a, src);
967  number q = n_Init(n, dst->extRing->cf);
968  poly p;
969  if (n_IsZero(q, dst->extRing->cf))
970  {
971    n_Delete(&q, dst->extRing->cf);
972    return NULL;
973  }
974  p = p_One(dst->extRing);
975  p_SetCoeff(p, q, dst->extRing);
976  fraction f = (fraction)omAlloc0Bin(fractionObjectBin);
977  NUM(f) = p; DEN(f) = NULL; COM(f) = 0;
978  return (number)f;
979}
980
981/* assumes that either src = Q(t_1, ..., t_s), dst = Q(t_1, ..., t_s), or
982                       src = Z/p(t_1, ..., t_s), dst = Z/p(t_1, ..., t_s) */
983number ntCopyMap(number a, const coeffs src, const coeffs dst)
984{
985  return ntCopy(a, dst);
986}
987
988/* assumes that src = Q, dst = Z/p(t_1, ..., t_s) */
989number ntMap0P(number a, const coeffs src, const coeffs dst)
990{
991  if (n_IsZero(a, src)) return NULL;
992  int p = rChar(dst->extRing);
993  int n = nlModP(a, p, src);
994  number q = n_Init(n, dst->extRing->cf);
995  poly g;
996  if (n_IsZero(q, dst->extRing->cf))
997  {
998    n_Delete(&q, dst->extRing->cf);
999    return NULL;
1000  }
1001  g = p_One(dst->extRing);
1002  p_SetCoeff(g, q, dst->extRing);
1003  fraction f = (fraction)omAlloc0Bin(fractionObjectBin);
1004  NUM(f) = g; DEN(f) = NULL; COM(f) = 0;
1005  return (number)f;
1006}
1007
1008/* assumes that src = Z/p, dst = Z/p(t_1, ..., t_s) */
1009number ntMapPP(number a, const coeffs src, const coeffs dst)
1010{
1011  if (n_IsZero(a, src)) return NULL;
1012  assume(src == dst->extRing->cf);
1013  poly p = p_One(dst->extRing);
1014  p_SetCoeff(p, ntCopy(a, src), dst->extRing);
1015  fraction f = (fraction)omAlloc0Bin(fractionObjectBin);
1016  NUM(f) = p; DEN(f) = NULL; COM(f) = 0;
1017  return (number)f;
1018}
1019
1020/* assumes that src = Z/u, dst = Z/p(t_1, ..., t_s), where u != p */
1021number ntMapUP(number a, const coeffs src, const coeffs dst)
1022{
1023  if (n_IsZero(a, src)) return NULL;
1024  /* mapping via intermediate int: */
1025  int n = n_Int(a, src);
1026  number q = n_Init(n, dst->extRing->cf);
1027  poly p;
1028  if (n_IsZero(q, dst->extRing->cf))
1029  {
1030    n_Delete(&q, dst->extRing->cf);
1031    return NULL;
1032  }
1033  p = p_One(dst->extRing);
1034  p_SetCoeff(p, q, dst->extRing);
1035  fraction f = (fraction)omAlloc0Bin(fractionObjectBin);
1036  NUM(f) = p; DEN(f) = NULL; COM(f) = 0;
1037  return (number)f;
1038}
1039
1040nMapFunc ntSetMap(const coeffs src, const coeffs dst)
1041{
1042  /* dst is expected to be a rational function field */
1043  assume(getCoeffType(dst) == ID);
1044 
1045  int h = 0; /* the height of the extension tower given by dst */
1046  coeffs bDst = nCoeff_bottom(dst, h); /* the bottom field in the tower dst */
1047 
1048  /* for the time being, we only provide maps if h = 1 and if b is Q or
1049     some field Z/pZ: */
1050  if (h != 1) return NULL;
1051  if ((!nCoeff_is_Zp(bDst)) && (!nCoeff_is_Q(bDst))) return NULL;
1052 
1053  /* Let T denote the sequence of transcendental extension variables, i.e.,
1054     K[t_1, ..., t_s] =: K[T];
1055     Let moreover, for any such sequence T, T' denote any subsequence of T
1056     of the form t_1, ..., t_w with w <= s. */
1057 
1058  if (nCoeff_is_Q(src) && nCoeff_is_Q(bDst))
1059    return ntMap00;                                      /// Q       -->  Q(T)
1060 
1061  if (nCoeff_is_Zp(src) && nCoeff_is_Q(bDst))
1062    return ntMapP0;                                      /// Z/p     -->  Q(T)
1063 
1064  if (nCoeff_is_Q(src) && nCoeff_is_Zp(bDst))
1065    return ntMap0P;                                      /// Q       --> Z/p(T)
1066 
1067  if (nCoeff_is_Zp(src) && nCoeff_is_Zp(bDst))
1068  {
1069    if (src->ch == dst->ch) return ntMapPP;              /// Z/p     --> Z/p(T)
1070    else return ntMapUP;                                 /// Z/u     --> Z/p(T)
1071  }
1072 
1073  coeffs bSrc = nCoeff_bottom(src, h); /* the bottom field in the tower src */
1074  if (h != 1) return NULL;
1075  if ((!nCoeff_is_Zp(bSrc)) && (!nCoeff_is_Q(bSrc))) return NULL;
1076 
1077  if (nCoeff_is_Q(bSrc) && nCoeff_is_Q(bDst))
1078  {
1079    if (rVar(src->extRing) > rVar(dst->extRing)) return NULL;
1080    for (int i = 0; i < rVar(src->extRing); i++)
1081      if (strcmp(rRingVar(i, src->extRing),
1082                 rRingVar(i, dst->extRing)) != 0) return NULL;
1083      return ntCopyMap;                                  /// Q(T')   --> Q(T)
1084  }
1085 
1086  if (nCoeff_is_Zp(bSrc) && nCoeff_is_Zp(bDst))
1087  {
1088    if (rVar(src->extRing) > rVar(dst->extRing)) return NULL;
1089    for (int i = 0; i < rVar(src->extRing); i++)
1090      if (strcmp(rRingVar(i, src->extRing),
1091                 rRingVar(i, dst->extRing)) != 0) return NULL;
1092      return ntCopyMap;                                  /// Z/p(T') --> Z/p(T)
1093  }
1094 
1095  return NULL;                                           /// default
1096}
1097
1098BOOLEAN ntInitChar(coeffs cf, void * infoStruct)
1099{
1100
1101  assume( cf != NULL );
1102  assume( infoStruct != NULL );
1103 
1104  TransExtInfo *e = (TransExtInfo *)infoStruct;
1105  /// first check whether cf->extRing != NULL and delete old ring???
1106  cf->extRing           = e->r;
1107  cf->extRing->minideal = NULL;
1108
1109  assume(cf->extRing                != NULL);      // extRing;
1110  assume(cf->extRing->cf            != NULL);      // extRing->cf;
1111  assume(getCoeffType(cf) == ID);                // coeff type;
1112 
1113  /* propagate characteristic up so that it becomes
1114     directly accessible in cf: */
1115  cf->ch = cf->extRing->cf->ch;
1116 
1117  cf->cfGreaterZero  = ntGreaterZero;
1118  cf->cfGreater      = ntGreater;
1119  cf->cfEqual        = ntEqual;
1120  cf->cfIsZero       = ntIsZero;
1121  cf->cfIsOne        = ntIsOne;
1122  cf->cfIsMOne       = ntIsMOne;
1123  cf->cfInit         = ntInit;
1124  cf->cfInt          = ntInt;
1125  cf->cfNeg          = ntNeg;
1126  cf->cfAdd          = ntAdd;
1127  cf->cfSub          = ntSub;
1128  cf->cfMult         = ntMult;
1129  cf->cfDiv          = ntDiv;
1130  cf->cfExactDiv     = ntDiv;
1131  cf->cfPower        = ntPower;
1132  cf->cfCopy         = ntCopy;
1133  cf->cfWrite        = ntWrite;
1134  cf->cfRead         = ntRead;
1135  cf->cfDelete       = ntDelete;
1136  cf->cfSetMap       = ntSetMap;
1137  cf->cfGetDenom     = ntGetDenom;
1138  cf->cfGetNumerator = ntGetNumerator;
1139  cf->cfRePart       = ntCopy;
1140  cf->cfImPart       = ntImPart;
1141  cf->cfCoeffWrite   = ntCoeffWrite;
1142#ifdef LDEBUG
1143  cf->cfDBTest       = ntDBTest;
1144#endif
1145  cf->cfGcd          = ntGcd;
1146  cf->cfLcm          = ntLcm;
1147  cf->cfSize         = ntSize;
1148  cf->nCoeffIsEqual  = ntCoeffIsEqual;
1149  cf->cfInvers       = ntInvers;
1150  cf->cfIntDiv       = ntDiv;
1151 
1152#ifndef HAVE_FACTORY
1153  PrintS("// Warning: The 'factory' module is not available.\n");
1154  PrintS("//          Hence gcd's cannot be cancelled in any\n");
1155  PrintS("//          computed fraction!\n");
1156#endif
1157 
1158  return FALSE;
1159}
1160
1161
1162number ntParam(const short iParameter, const coeffs cf)
1163{
1164  assume(getCoeffType(cf) == ID);
1165
1166  const ring R = cf->extRing;
1167  assume( R != NULL ); 
1168  assume( 0 < iParameter && iParameter <= rVar(R) );
1169
1170  poly p = p_One(R); p_SetExp(p, iParameter, 1, R); p_Setm(p, R);
1171
1172//  return (number) p;
1173
1174  fraction f = (fraction)omAlloc0Bin(fractionObjectBin);
1175  NUM(f) = p;
1176  DEN(f) = NULL;
1177  COM(f) = 0;
1178
1179  return (number)f;
1180}
1181
1182
1183/// if m == var(i)/1 => return i,
1184int ntIsParam(number m, const coeffs cf)
1185{
1186  assume(getCoeffType(cf) == ID);
1187
1188  const ring R = cf->extRing;
1189  assume( R != NULL );
1190
1191  fraction f = (fraction)m;
1192
1193  if( DEN(f) != NULL )
1194    return 0;
1195
1196  return p_Var( NUM(f), R ); 
1197}
Note: See TracBrowser for help on using the repository browser.